natureco-cli 5.64.0 → 5.64.2
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 +28 -0
- package/README.md +21 -3
- package/package.json +2 -1
- package/scripts/e2e-context-smoke.js +33 -0
- package/src/commands/code_v5.js +10 -1
- package/src/commands/help.js +11 -1
- package/src/tools/computer_use.js +12 -3
- package/src/tools/computer_use_loop.js +23 -16
- package/src/tools/workflow.js +6 -3
- package/src/utils/conversation-context.js +52 -0
- package/src/utils/token-budget.js +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NatureCo CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [5.64.2] - 2026-07-13 — Visible and reliable macOS GUI automation
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- REPL workflow tool activity no longer disappears when the next thinking indicator starts; every tool name and its success/failure marker remains visible in the transcript.
|
|
9
|
+
- Multi-step visual desktop tasks are routed to the screenshot-driven `computer_use_loop` instead of blind single click/type calls.
|
|
10
|
+
- The visual loop now uses the canonical provider endpoint builder, avoiding duplicated `/v1/v1` paths with MiniMax configurations.
|
|
11
|
+
- macOS special keys such as Enter, Tab and Escape now use correct AppleScript key codes.
|
|
12
|
+
- Screenshot capture and GUI-loop failures can no longer be reported as successful completion; missing files, failed actions and unverified max-step exits return explicit failures.
|
|
13
|
+
|
|
14
|
+
### Tests
|
|
15
|
+
- Added regression coverage for GUI-loop action failures and MiniMax endpoint construction with and without a trailing `/v1`.
|
|
16
|
+
|
|
17
|
+
## [5.64.1] - 2026-07-13 — Reliable coding context and token-budgeted follow-ups
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- `natureco code` now passes prior user/assistant turns into each workflow call, so follow-up requests correctly refer to files and work created earlier in the same session.
|
|
21
|
+
- Workflow history excludes system/tool internals, empty messages and stale turns; oversized recent responses are safely truncated instead of being resent in full.
|
|
22
|
+
- The help screen now parses provider URLs correctly (`api.minimax.io` instead of `https:`).
|
|
23
|
+
|
|
24
|
+
### Token economy
|
|
25
|
+
- Added workflow-history budgets to all profiles: Efficient 1,024 tokens, Balanced 2,048 tokens and Quality 8,192 tokens.
|
|
26
|
+
- Long generated HTML/code no longer dominates every later prompt. A synthetic 32,000-character response is bounded from roughly 8,000 repeated tokens to 2,048 in Balanced mode (approximately 74% less repeated context).
|
|
27
|
+
- Preserved recent-turn continuity while keeping token use bounded by both message count and estimated tokens.
|
|
28
|
+
|
|
29
|
+
### Documentation and tests
|
|
30
|
+
- Updated README release highlights, tool count, coding-session behavior and token-economy documentation.
|
|
31
|
+
- Added regression coverage for same-session workflow context, role filtering, recency, oversized response truncation and provider hostname rendering.
|
|
32
|
+
|
|
5
33
|
## [5.64.0] - 2026-07-13 — Secure unified agent and operations foundation
|
|
6
34
|
|
|
7
35
|
### Added
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
A terminal-native AI agent CLI — chat, write code, automate workflows, and connect **Telegram / Discord / Slack / WhatsApp / iMessage** and more.
|
|
14
14
|
|
|
15
|
-
**A Claude Code & OpenClaw alternative** · Multi-agent orchestration · Cross-session memory · Dangerous-command approval · 12 providers, 200+ models ·
|
|
15
|
+
**A Claude Code & OpenClaw alternative** · Multi-agent orchestration · Cross-session memory · Token-budgeted context · Dangerous-command approval · 12 providers, 200+ models · 106 tools · 10 messaging channels.
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
███╗ ██╗ █████╗ ████████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
|
|
@@ -51,6 +51,9 @@ natureco code
|
|
|
51
51
|
|
|
52
52
|
| Version | Highlights |
|
|
53
53
|
|---------|-----------|
|
|
54
|
+
| **v5.64.2** | **Reliable macOS GUI automation:** tool names stay visible in REPL transcripts, visual tasks use a verified screenshot loop, MiniMax GUI routing avoids duplicate paths, and failed/unverified actions no longer claim success. |
|
|
55
|
+
| **v5.64.1** | **Reliable follow-ups + lower token cost:** `natureco code` now preserves same-session context across workflow calls and caps repeated history by token budget (1,024 / 2,048 / 8,192). Provider labels are rendered correctly. |
|
|
56
|
+
| **v5.64.0** | **Unified secure agent foundation:** one execution gateway, hard-stop guardrails, schema-validated tools, rollback/checkpoints, sourced memory, resilient channel delivery, OS keychains and encrypted sync. |
|
|
54
57
|
| **v5.43.0** | **Security:** 9 vulnerabilities fixed in a 3-round audit (RCE chain, admin-rpc auth, cron persistence, channel access control). See [`SECURITY_AUDIT_SUMMARY.md`](SECURITY_AUDIT_SUMMARY.md). |
|
|
55
58
|
| **v5.42.0** | **Token optimization** — prompts trimmed by ~76% (skill index made compact; big cost savings on multi-step tasks). |
|
|
56
59
|
| **v5.41.0** | **Multi-agent orchestration** — the agent can spawn focused sub-agents (`sub_agent`) and produce step-by-step plans (`plan`) before acting. |
|
|
@@ -102,11 +105,12 @@ Two-tier policy (`deny` / `allowlist` / `full`) applies to **every** shell path
|
|
|
102
105
|
## ✨ Features
|
|
103
106
|
|
|
104
107
|
### 🤖 AI & Chat
|
|
105
|
-
- **
|
|
108
|
+
- **106 tools** — file ops, web search, image generation, code execution, memory, automation, channels, and more
|
|
106
109
|
- **Interactive REPL** — read_file, edit_file, bash, multi-turn conversation
|
|
107
110
|
- **Slash commands** — `/memory`, `/help`, `/skills`, `/model`, `/clear`
|
|
108
111
|
- **Streaming output** with live tool-call visibility and a thinking indicator
|
|
109
112
|
- **Persistent memory** — fact-based, cross-session
|
|
113
|
+
- **Token-budgeted context** — recent relevant turns are retained without repeatedly sending oversized code and tool output
|
|
110
114
|
|
|
111
115
|
### 💻 Coding Agent (Claude Code alternative)
|
|
112
116
|
- **Read / Write / Edit** multi-file operations
|
|
@@ -114,6 +118,20 @@ Two-tier policy (`deny` / `allowlist` / `full`) applies to **every** shell path
|
|
|
114
118
|
- **Multi-agent orchestration** — spawn focused sub-agents and plan before acting
|
|
115
119
|
- **Skills** — progressive-disclosure expertise loaded on demand via `skill_view`
|
|
116
120
|
- **Verify loop** — the agent runs and tests the code it writes
|
|
121
|
+
- **Reliable follow-ups** — references such as “the game you just created” retain the current coding-session context
|
|
122
|
+
- **Context profiles** — Efficient (1,024), Balanced (2,048), and Quality (8,192) workflow-history token budgets
|
|
123
|
+
|
|
124
|
+
### ⚡ Token Economy
|
|
125
|
+
|
|
126
|
+
NatureCo uses progressive disclosure and bounded context instead of sending every skill, tool result, and old response on every request:
|
|
127
|
+
|
|
128
|
+
- Skills are discovered with `skill_find` and loaded only when needed with `skill_view`.
|
|
129
|
+
- System, internal tool, and empty messages are excluded from workflow history.
|
|
130
|
+
- Recent user/assistant turns are kept within the selected token profile.
|
|
131
|
+
- Oversized generated code is truncated in later prompts while the file path and conversational intent remain available.
|
|
132
|
+
- `natureco ask` stays tool-free by default; use `--tools` only when an action is required.
|
|
133
|
+
|
|
134
|
+
For a 32,000-character previous response, the Balanced profile bounds repeated history to about 2,048 tokens instead of roughly 8,000—a reduction of approximately 74% for that repeated context.
|
|
117
135
|
|
|
118
136
|
### 📡 10 Messaging Channels
|
|
119
137
|
|
|
@@ -161,7 +179,7 @@ Two-tier policy (`deny` / `allowlist` / `full`) applies to **every** shell path
|
|
|
161
179
|
|
|
162
180
|
| Command | Description |
|
|
163
181
|
|---------|-------------|
|
|
164
|
-
| `natureco chat` | Interactive REPL chat (
|
|
182
|
+
| `natureco chat` | Interactive REPL chat (106 tools available) |
|
|
165
183
|
| `natureco chat --resume` | Resume the previous session |
|
|
166
184
|
| `natureco code` | Coding agent (write apps/scripts) |
|
|
167
185
|
| `natureco code <file>` | Coding agent on a specific file |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.64.
|
|
3
|
+
"version": "5.64.2",
|
|
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"
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"lint:fix": "eslint src/ bin/ test/ --fix",
|
|
28
28
|
"lint:errors-only": "eslint src/ bin/ test/ --quiet",
|
|
29
29
|
"smoke": "node --check bin/natureco.js && node bin/natureco.js help",
|
|
30
|
+
"smoke:context": "node scripts/e2e-context-smoke.js",
|
|
30
31
|
"postinstall": "node scripts/postinstall.js",
|
|
31
32
|
"prepublishOnly": "node --check bin/natureco.js && eslint src/ bin/ test/ --quiet && vitest run"
|
|
32
33
|
},
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const workflow = require('../src/tools/workflow');
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
const marker = `natureco-context-${Date.now()}.html`;
|
|
7
|
+
const firstTask = `Bu test oturumundaki dosya adi ${marker}. Arac calistirma; sadece dosya adini iceren kisa bir onay ver.`;
|
|
8
|
+
const first = await workflow.execute({ action: 'run', task: firstTask });
|
|
9
|
+
if (!first || first.success === false || !first.reply) {
|
|
10
|
+
throw new Error(`First turn failed: ${first?.error || 'empty reply'}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const history = [
|
|
14
|
+
{ role: 'user', content: firstTask },
|
|
15
|
+
{ role: 'assistant', content: String(first.reply) },
|
|
16
|
+
];
|
|
17
|
+
const second = await workflow.execute({
|
|
18
|
+
action: 'run',
|
|
19
|
+
task: 'Az once konustugumuz dosyanin tam adi neydi? Yalnizca dosya adini soyle.',
|
|
20
|
+
conversationHistory: history,
|
|
21
|
+
});
|
|
22
|
+
const reply = String(second?.reply || '');
|
|
23
|
+
if (!second || second.success === false || !reply.includes(marker)) {
|
|
24
|
+
throw new Error(`Context recall failed. Expected ${marker}; received: ${reply.slice(0, 240)}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(JSON.stringify({ ok: true, marker, reply: reply.trim() }));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
main().catch(error => {
|
|
31
|
+
console.error(error.message);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
});
|
package/src/commands/code_v5.js
CHANGED
|
@@ -34,6 +34,8 @@ const { getTaskManager } = require("../utils/tasks");
|
|
|
34
34
|
const { buildTiers, assemble, discoverProjectRules } = require("../utils/system-prompt");
|
|
35
35
|
const { buildSkillIndex } = require("../utils/skill-index");
|
|
36
36
|
const { AgentCore } = require("../utils/agent-core");
|
|
37
|
+
const { prepareConversationHistory } = require("../utils/conversation-context");
|
|
38
|
+
const tokenBudget = require("../utils/token-budget");
|
|
37
39
|
|
|
38
40
|
const agentCore = new AgentCore({ maxIterations: 10 });
|
|
39
41
|
|
|
@@ -522,7 +524,14 @@ async function codeV5(targetPath) {
|
|
|
522
524
|
|
|
523
525
|
// Workflow step — run before every request
|
|
524
526
|
process.stdout.write(tui.styled('\r 🔧 workflow... ', { color: tui.PALETTE.muted }));
|
|
525
|
-
const wfResult = await executeTool('workflow', {
|
|
527
|
+
const wfResult = await executeTool('workflow', {
|
|
528
|
+
action: 'run',
|
|
529
|
+
task: input,
|
|
530
|
+
conversationHistory: prepareConversationHistory(messages, {
|
|
531
|
+
maxMessages: tokenBudget.load().conversationInContext,
|
|
532
|
+
maxTokens: tokenBudget.load().workflowHistoryMaxTokens,
|
|
533
|
+
}),
|
|
534
|
+
}, toolDefs);
|
|
526
535
|
const wf = wfResult?.result || {};
|
|
527
536
|
if (wf.success !== false) {
|
|
528
537
|
const loaded = wf.skillsLoaded && wf.skillsLoaded.length > 0 ? ` [skill: ${wf.skillsLoaded.join(', ')}]` : '';
|
package/src/commands/help.js
CHANGED
|
@@ -12,6 +12,15 @@ const { getLang } = require('../utils/i18n');
|
|
|
12
12
|
|
|
13
13
|
const L = (tr, en) => (getLang() === 'en' ? en : tr);
|
|
14
14
|
|
|
15
|
+
function providerHostname(providerUrl) {
|
|
16
|
+
if (!providerUrl) return '';
|
|
17
|
+
try {
|
|
18
|
+
return new URL(providerUrl).hostname;
|
|
19
|
+
} catch {
|
|
20
|
+
return String(providerUrl).replace(/^https?:\/\//i, '').split('/')[0];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
function help() {
|
|
16
25
|
const config = getConfig() || {};
|
|
17
26
|
const version = require('../../package.json').version;
|
|
@@ -203,7 +212,7 @@ function help() {
|
|
|
203
212
|
tui.styled(' ╭' + '─'.repeat(cardW) + '╮', { color: tui.PALETTE.border }),
|
|
204
213
|
];
|
|
205
214
|
if (config.providerUrl) {
|
|
206
|
-
const provider = config.providerUrl
|
|
215
|
+
const provider = providerHostname(config.providerUrl);
|
|
207
216
|
cardLines.push(tui.styled(' │ ', { color: tui.PALETTE.border }) + tui.C.muted('Provider ') + tui.styled(provider.padEnd(38), { color: tui.PALETTE.text, bold: true }) + tui.styled(' │', { color: tui.PALETTE.border }));
|
|
208
217
|
}
|
|
209
218
|
if (config.providerModel) {
|
|
@@ -228,3 +237,4 @@ function help() {
|
|
|
228
237
|
}
|
|
229
238
|
|
|
230
239
|
module.exports = help;
|
|
240
|
+
module.exports.providerHostname = providerHostname;
|
|
@@ -77,7 +77,9 @@ async function computerUse(params) {
|
|
|
77
77
|
const outputFile = file || path.join(os.tmpdir(), 'natureco_screen_' + Date.now() + '.png');
|
|
78
78
|
try {
|
|
79
79
|
if (PLATFORM === 'darwin') {
|
|
80
|
-
spawnSync('screencapture', ['-x', outputFile], { timeout: 5000 });
|
|
80
|
+
const capture = spawnSync('screencapture', ['-x', outputFile], { timeout: 5000, encoding: 'utf8' });
|
|
81
|
+
if (capture.error) throw capture.error;
|
|
82
|
+
if (capture.status !== 0) throw new Error(capture.stderr || `screencapture exit ${capture.status}`);
|
|
81
83
|
} else if (PLATFORM === 'win32') {
|
|
82
84
|
spawnSync('powershell', ['-Command',
|
|
83
85
|
'Add-Type -AssemblyName System.Windows.Forms; ' +
|
|
@@ -89,7 +91,13 @@ async function computerUse(params) {
|
|
|
89
91
|
} else {
|
|
90
92
|
spawnSync('import', ['-window', 'root', outputFile], { timeout: 5000 });
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
if (!fs.existsSync(outputFile)) throw new Error('Screenshot file was not created');
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
file: outputFile,
|
|
98
|
+
platform: PLATFORM,
|
|
99
|
+
note: 'Screenshot saved. This tool returns a file path, not visual analysis; use computer_use_loop for autonomous visual interaction.',
|
|
100
|
+
};
|
|
93
101
|
} catch (e) {
|
|
94
102
|
return { success: false, error: 'Screenshot hatasi: ' + e.message };
|
|
95
103
|
}
|
|
@@ -151,9 +159,10 @@ async function computerUse(params) {
|
|
|
151
159
|
}
|
|
152
160
|
|
|
153
161
|
if (KEY_MAP_DARWIN[actualKey]) {
|
|
162
|
+
const keyCodes = { return: 36, tab: 48, escape: 53, up: 126, down: 125, left: 123, right: 124, delete: 51, forwarddelete: 117, home: 115, end: 119, 'page up': 116, 'page down': 121, space: 49 };
|
|
154
163
|
const keyName = KEY_MAP_DARWIN[actualKey];
|
|
155
164
|
const usingClause = mods.length > 0 ? ' using {' + mods.join(', ') + '}' : '';
|
|
156
|
-
const r = osaScript('tell application "System Events" to
|
|
165
|
+
const r = osaScript('tell application "System Events" to key code ' + keyCodes[keyName] + usingClause);
|
|
157
166
|
if (!r.success) return r;
|
|
158
167
|
} else if (actualKey) {
|
|
159
168
|
const usingClause = mods.length > 0 ? ' using {' + mods.join(', ') + '}' : '';
|
|
@@ -9,22 +9,15 @@ const https = require('https');
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const os = require('os');
|
|
12
|
+
const { buildChatEndpoint } = require('../utils/provider-detect');
|
|
12
13
|
|
|
13
14
|
function loadConfig() {
|
|
14
15
|
try { return JSON.parse(fs.readFileSync(path.join(os.homedir(), '.natureco', 'config.json'), 'utf8')); } catch { return {}; }
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
function isMiniMax(url) { return url && (url.includes('minimax.io') || url.includes('minimaxi.com') || url.includes('minimax.cn')); }
|
|
18
|
-
function isGemini(url) { return url && (url.includes('generativelanguage.googleapis.com') || url.includes('gemini')); }
|
|
19
|
-
|
|
20
18
|
function apiCall(providerUrl, apiKey, body) {
|
|
21
19
|
return new Promise((resolve, reject) => {
|
|
22
|
-
const
|
|
23
|
-
const endpoint = isMiniMax(base)
|
|
24
|
-
? base + '/v1/text/chatcompletion_v2'
|
|
25
|
-
: isGemini(base)
|
|
26
|
-
? base + '/openai/chat/completions'
|
|
27
|
-
: base + '/chat/completions';
|
|
20
|
+
const endpoint = buildChatEndpoint(providerUrl);
|
|
28
21
|
const req = https.request(endpoint, {
|
|
29
22
|
method: 'POST',
|
|
30
23
|
headers: { 'Authorization': 'Bearer ' + apiKey, 'Content-Type': 'application/json' },
|
|
@@ -46,6 +39,7 @@ function apiCall(providerUrl, apiKey, body) {
|
|
|
46
39
|
}
|
|
47
40
|
|
|
48
41
|
function screenshotBase64() {
|
|
42
|
+
if (os.platform() !== 'darwin') throw new Error('computer_use_loop currently requires macOS');
|
|
49
43
|
const file = path.join(os.tmpdir(), 'ncloop_' + Date.now() + '.png');
|
|
50
44
|
require('child_process').execSync('screencapture -x "' + file + '"', { timeout: 5000 });
|
|
51
45
|
const buf = fs.readFileSync(file);
|
|
@@ -73,7 +67,7 @@ function executeAction(action, params) {
|
|
|
73
67
|
case 'type':
|
|
74
68
|
return osaScript('tell application "System Events" to keystroke "' + ESC(params.text) + '"');
|
|
75
69
|
case 'keypress': {
|
|
76
|
-
const
|
|
70
|
+
const KEY_CODES = { enter: 36, return: 36, tab: 48, escape: 53, up: 126, down: 125, left: 123, right: 124, backspace: 51, delete: 117, space: 49 };
|
|
77
71
|
const MOD_MAP = { cmd: 'command down', command: 'command down', option: 'option down', alt: 'option down', ctrl: 'control down', shift: 'shift down' };
|
|
78
72
|
const parts = params.key.toLowerCase().split('+').map(p => p.trim());
|
|
79
73
|
const mods = [];
|
|
@@ -82,9 +76,12 @@ function executeAction(action, params) {
|
|
|
82
76
|
if (MOD_MAP[p]) mods.push(MOD_MAP[p]);
|
|
83
77
|
else actual = p;
|
|
84
78
|
}
|
|
85
|
-
const k = KEY_MAP[actual] || actual;
|
|
86
79
|
const using = mods.length > 0 ? ' using {' + mods.join(', ') + '}' : '';
|
|
87
|
-
|
|
80
|
+
if (Object.prototype.hasOwnProperty.call(KEY_CODES, actual)) {
|
|
81
|
+
return osaScript('tell application "System Events" to key code ' + KEY_CODES[actual] + using);
|
|
82
|
+
}
|
|
83
|
+
if (!actual) return { success: false, error: 'A key is required with the modifier' };
|
|
84
|
+
return osaScript('tell application "System Events" to keystroke "' + ESC(actual) + '"' + using);
|
|
88
85
|
}
|
|
89
86
|
case 'mouse_move':
|
|
90
87
|
return osaScript('tell application "System Events" to set position of mouse to {' + params.x + ', ' + params.y + '}');
|
|
@@ -122,10 +119,15 @@ async function loop(goal, maxSteps) {
|
|
|
122
119
|
const model = cfg.providerModel || 'default';
|
|
123
120
|
|
|
124
121
|
if (!providerUrl || !apiKey) {
|
|
125
|
-
return
|
|
122
|
+
return { success: false, error: 'Provider not configured' };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (os.platform() !== 'darwin') {
|
|
126
|
+
return { success: false, error: 'computer_use_loop currently requires macOS' };
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
const steps = [];
|
|
130
|
+
let completed = false;
|
|
129
131
|
for (let i = 0; i < maxSteps; i++) {
|
|
130
132
|
try {
|
|
131
133
|
// 1. Screenshot
|
|
@@ -175,6 +177,7 @@ async function loop(goal, maxSteps) {
|
|
|
175
177
|
// 3. Execute
|
|
176
178
|
if (decision.action === 'done') {
|
|
177
179
|
steps.push({ step: i + 1, action: 'done', reason: decision.reason || 'Goal achieved' });
|
|
180
|
+
completed = true;
|
|
178
181
|
break;
|
|
179
182
|
}
|
|
180
183
|
|
|
@@ -187,6 +190,7 @@ async function loop(goal, maxSteps) {
|
|
|
187
190
|
const retryResult = executeAction(decision.action, decision);
|
|
188
191
|
if (!retryResult.success) {
|
|
189
192
|
steps.push({ step: i + 1, action: 'error', error: execResult.error });
|
|
193
|
+
return { success: false, error: execResult.error, goal, totalSteps: steps.length, steps };
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
@@ -195,7 +199,7 @@ async function loop(goal, maxSteps) {
|
|
|
195
199
|
|
|
196
200
|
// Safety: if too many steps, break
|
|
197
201
|
if (i >= maxSteps - 1) {
|
|
198
|
-
steps.push({ step: i + 1, action: '
|
|
202
|
+
steps.push({ step: i + 1, action: 'error', error: 'Max steps reached before the goal was verified' });
|
|
199
203
|
}
|
|
200
204
|
} catch (e) {
|
|
201
205
|
steps.push({ step: i + 1, action: 'error', error: e.message });
|
|
@@ -203,7 +207,10 @@ async function loop(goal, maxSteps) {
|
|
|
203
207
|
}
|
|
204
208
|
}
|
|
205
209
|
|
|
206
|
-
|
|
210
|
+
if (!completed) {
|
|
211
|
+
return { success: false, error: 'Goal was not verified', goal, totalSteps: steps.length, steps };
|
|
212
|
+
}
|
|
213
|
+
return { success: true, goal, totalSteps: steps.length, steps };
|
|
207
214
|
}
|
|
208
215
|
|
|
209
216
|
const name = 'computer_use_loop';
|
|
@@ -221,4 +228,4 @@ async function execute(params) {
|
|
|
221
228
|
return await loop(params.goal, params.maxSteps || 30);
|
|
222
229
|
}
|
|
223
230
|
|
|
224
|
-
module.exports = { name, description, parameters, execute };
|
|
231
|
+
module.exports = { name, description, parameters, execute, executeAction };
|
package/src/tools/workflow.js
CHANGED
|
@@ -300,7 +300,7 @@ async function workflow(params) {
|
|
|
300
300
|
'- Dosya degistirmeden ONCE read_file ile oku, edit_file ile hedefli degistir. Yerini bilmiyorsan file_search/grep_search ile kesfet.',
|
|
301
301
|
'- Kod yazinca/degistirince bash ile calistir/test et, hata varsa duzelt. Coklu dosya = her biri icin AYRI write_file. Hep TAM yol; "masaustu" = ' + desktop + '.',
|
|
302
302
|
'- Arac sonuclari <tool_results> icinde doner; is bitince arac cagirmadan tek cumlelik ozet yaz. Basit sohbette arac cagirma, dogrudan yanitla.',
|
|
303
|
-
execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir.' : '',
|
|
303
|
+
execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir. Gorsel geri bildirim gerektiren cok adimli GUI gorevlerinde tekil screenshot/click yerine computer_use_loop kullan; basariyi arac dogrulamadan tamamlandi deme.' : '',
|
|
304
304
|
fullToolsBlock,
|
|
305
305
|
treeDigest ? ('\n\nBILDIGIN KALICI HAFIZA (bu kullaniciya ait, onceki oturumlardan hatirladiklarin; kullaniciya ozel bir sey sorulursa ONCE BUNU KULLAN — dosya arama, uydurma):\n' + treeDigest) : '',
|
|
306
306
|
treeIndex ? ('\n\nHafiza agaci yapisi (yukarida olmayan detay icin memory_tree(action:read/search) ile ilgili kok/dali oku):\n' + treeIndex) : '',
|
|
@@ -337,7 +337,7 @@ async function workflow(params) {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
// Araç aktivitesi gösterimi (TTY streaming): her araç icin "🔧 label · hint ✓/✗"
|
|
340
|
-
const TOOL_LABEL = { write_file: 'dosya yaz', read_file: 'oku', edit_file: 'düzenle', bash: 'komut', file_search: 'ara', list_dir: 'listele', skill_view: 'skill', browser: 'tarayıcı', browser_use: 'tarayıcı', mac_app_open: 'uygulama aç', mac_app_quit: 'uygulama kapat', computer_use: 'GUI', social_open: 'medya aç', macos_screenshot: 'ekran görüntüsü' };
|
|
340
|
+
const TOOL_LABEL = { write_file: 'dosya yaz', read_file: 'oku', edit_file: 'düzenle', bash: 'komut', file_search: 'ara', list_dir: 'listele', skill_view: 'skill', browser: 'tarayıcı', browser_use: 'tarayıcı', mac_app_open: 'uygulama aç', mac_app_quit: 'uygulama kapat', computer_use: 'GUI', computer_use_loop: 'GUI görsel döngü', social_open: 'medya aç', macos_screenshot: 'ekran görüntüsü' };
|
|
341
341
|
function briefHint(args) {
|
|
342
342
|
if (!args || typeof args !== 'object') return '';
|
|
343
343
|
const v = args.appName || args.query || args.name || args.url || args.command || args.pattern || args.path || args.action;
|
|
@@ -350,7 +350,10 @@ async function workflow(params) {
|
|
|
350
350
|
process.stdout.write('\n\x1b[2m 🔧 ' + label + (hint ? ' · ' + hint : '') + '\x1b[0m');
|
|
351
351
|
} else {
|
|
352
352
|
const rec = (ev.records || [])[0] || {};
|
|
353
|
-
|
|
353
|
+
// Satırı burada bitir. Sonraki model çağrısının thinking göstergesi
|
|
354
|
+
// mevcut satırı \r + erase-line ile temizlediği için newline yoksa
|
|
355
|
+
// kullanıcı araç adını ve sonucunu hiç göremiyordu.
|
|
356
|
+
process.stdout.write((rec.status === 'done' ? ' \x1b[32m✓\x1b[0m' : ' \x1b[31m✗\x1b[0m') + '\n');
|
|
354
357
|
}
|
|
355
358
|
} : null;
|
|
356
359
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function estimateTokens(content) {
|
|
4
|
+
if (!content) return 0;
|
|
5
|
+
return Math.ceil(String(content).length / 4);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function prepareConversationHistory(messages, options = {}) {
|
|
9
|
+
if (!Array.isArray(messages)) return [];
|
|
10
|
+
|
|
11
|
+
if (typeof options === 'number') options = { maxMessages: options };
|
|
12
|
+
const maxMessages = Math.max(1, options.maxMessages || 20);
|
|
13
|
+
const maxTokens = Math.max(1, options.maxTokens || 2048);
|
|
14
|
+
|
|
15
|
+
const candidates = messages
|
|
16
|
+
.filter(message =>
|
|
17
|
+
message &&
|
|
18
|
+
(message.role === 'user' || message.role === 'assistant') &&
|
|
19
|
+
typeof message.content === 'string' &&
|
|
20
|
+
message.content.trim()
|
|
21
|
+
)
|
|
22
|
+
.slice(-maxMessages);
|
|
23
|
+
|
|
24
|
+
const selected = [];
|
|
25
|
+
let remainingTokens = maxTokens;
|
|
26
|
+
|
|
27
|
+
for (let i = candidates.length - 1; i >= 0 && remainingTokens > 0; i--) {
|
|
28
|
+
const message = candidates[i];
|
|
29
|
+
const content = message.content.trim();
|
|
30
|
+
const contentTokens = estimateTokens(content);
|
|
31
|
+
if (contentTokens <= remainingTokens) {
|
|
32
|
+
selected.unshift({ role: message.role, content });
|
|
33
|
+
remainingTokens -= contentTokens;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Preserve the newest oversized turn, but never let a generated file or
|
|
38
|
+
// verbose tool summary consume the entire next request's context budget.
|
|
39
|
+
if (selected.length === 0) {
|
|
40
|
+
const maxChars = Math.max(4, remainingTokens * 4);
|
|
41
|
+
selected.unshift({
|
|
42
|
+
role: message.role,
|
|
43
|
+
content: content.slice(0, Math.max(0, maxChars - 36)) + '\n[... context truncated ...]',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return selected;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { estimateTokens, prepareConversationHistory };
|
|
@@ -22,6 +22,7 @@ const PRESETS = {
|
|
|
22
22
|
fileContentMaxChars: 1000,
|
|
23
23
|
conversationOnDisk: 6,
|
|
24
24
|
conversationInContext: 8,
|
|
25
|
+
workflowHistoryMaxTokens: 1024,
|
|
25
26
|
autoCompact: true,
|
|
26
27
|
compactModel: null,
|
|
27
28
|
reservedTokens: 1024
|
|
@@ -42,6 +43,7 @@ const PRESETS = {
|
|
|
42
43
|
fileContentMaxChars: 2000,
|
|
43
44
|
conversationOnDisk: 10,
|
|
44
45
|
conversationInContext: 12,
|
|
46
|
+
workflowHistoryMaxTokens: 2048,
|
|
45
47
|
autoCompact: true,
|
|
46
48
|
compactModel: null,
|
|
47
49
|
reservedTokens: 2048
|
|
@@ -62,6 +64,7 @@ const PRESETS = {
|
|
|
62
64
|
fileContentMaxChars: 5000,
|
|
63
65
|
conversationOnDisk: 20,
|
|
64
66
|
conversationInContext: 25,
|
|
67
|
+
workflowHistoryMaxTokens: 8192,
|
|
65
68
|
autoCompact: false,
|
|
66
69
|
compactModel: null,
|
|
67
70
|
reservedTokens: 4096
|