natureco-cli 5.6.48 → 5.7.1

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.
Files changed (172) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/DEPLOY_v2.0.0.md +0 -0
  3. package/LICENSE +21 -0
  4. package/README.md +2 -2
  5. package/README.md.bak +0 -0
  6. package/bin/natureco.js +0 -0
  7. package/package.json +14 -6
  8. package/skills/code-review/SKILL.md +0 -0
  9. package/skills/summarize/SKILL.md +0 -0
  10. package/skills/translate/SKILL.md +0 -0
  11. package/src/commands/acp.js +0 -0
  12. package/src/commands/admin-rpc.js +0 -0
  13. package/src/commands/agent.js +0 -0
  14. package/src/commands/agents.js +0 -0
  15. package/src/commands/approvals.js +0 -0
  16. package/src/commands/ask.js +0 -0
  17. package/src/commands/backup.js +0 -0
  18. package/src/commands/bonjour.js +0 -0
  19. package/src/commands/bots.js +0 -0
  20. package/src/commands/browser.js +0 -0
  21. package/src/commands/capability.js +0 -0
  22. package/src/commands/channels.js +0 -0
  23. package/src/commands/chat.js +5 -0
  24. package/src/commands/clawbot.js +0 -0
  25. package/src/commands/clickclack.js +0 -0
  26. package/src/commands/code.js +0 -0
  27. package/src/commands/commands.js +0 -0
  28. package/src/commands/commitments.js +0 -0
  29. package/src/commands/completion.js +0 -0
  30. package/src/commands/config.js +4 -0
  31. package/src/commands/configure.js +0 -0
  32. package/src/commands/crestodian.js +0 -0
  33. package/src/commands/cron.js +0 -0
  34. package/src/commands/daemon.js +0 -0
  35. package/src/commands/dashboard.js +0 -0
  36. package/src/commands/device-pair.js +0 -0
  37. package/src/commands/devices.js +0 -0
  38. package/src/commands/directory.js +0 -0
  39. package/src/commands/discord.js +0 -0
  40. package/src/commands/dns.js +0 -0
  41. package/src/commands/docs.js +0 -0
  42. package/src/commands/doctor.js +0 -0
  43. package/src/commands/exec-policy.js +0 -0
  44. package/src/commands/gateway-server.js +38 -3
  45. package/src/commands/gateway.js +2 -0
  46. package/src/commands/git.js +0 -0
  47. package/src/commands/health.js +0 -0
  48. package/src/commands/help.js +0 -0
  49. package/src/commands/hooks.js +0 -0
  50. package/src/commands/imessage.js +0 -0
  51. package/src/commands/infer.js +0 -0
  52. package/src/commands/init.js +0 -0
  53. package/src/commands/irc.js +0 -0
  54. package/src/commands/login.js +0 -0
  55. package/src/commands/logout.js +0 -0
  56. package/src/commands/logs.js +0 -0
  57. package/src/commands/mattermost.js +0 -0
  58. package/src/commands/mcp.js +0 -0
  59. package/src/commands/memory-cmd.js +0 -0
  60. package/src/commands/memory.js +0 -0
  61. package/src/commands/message.js +0 -0
  62. package/src/commands/migrate.js +0 -0
  63. package/src/commands/models.js +0 -0
  64. package/src/commands/node.js +0 -0
  65. package/src/commands/nodes.js +4 -0
  66. package/src/commands/oc-path.js +0 -0
  67. package/src/commands/onboard.js +0 -0
  68. package/src/commands/open-prose.js +0 -0
  69. package/src/commands/pairing.js +0 -0
  70. package/src/commands/path.js +0 -0
  71. package/src/commands/plugins.js +0 -0
  72. package/src/commands/policy.js +0 -0
  73. package/src/commands/proxy.js +0 -0
  74. package/src/commands/qr.js +0 -0
  75. package/src/commands/repl.js +13 -18
  76. package/src/commands/reset.js +0 -0
  77. package/src/commands/run.js +0 -0
  78. package/src/commands/sandbox.js +0 -0
  79. package/src/commands/secrets.js +0 -0
  80. package/src/commands/security.js +0 -0
  81. package/src/commands/sessions.js +0 -0
  82. package/src/commands/setup.js +7 -6
  83. package/src/commands/signal.js +0 -0
  84. package/src/commands/skills.js +0 -0
  85. package/src/commands/slack.js +0 -0
  86. package/src/commands/sms.js +0 -0
  87. package/src/commands/status.js +0 -0
  88. package/src/commands/system.js +0 -0
  89. package/src/commands/tasks.js +0 -0
  90. package/src/commands/telegram.js +0 -0
  91. package/src/commands/terminal.js +0 -0
  92. package/src/commands/thread-ownership.js +0 -0
  93. package/src/commands/transcripts.js +0 -0
  94. package/src/commands/tui.js +0 -0
  95. package/src/commands/ultrareview.js +0 -0
  96. package/src/commands/uninstall.js +0 -0
  97. package/src/commands/update.js +0 -0
  98. package/src/commands/voice.js +0 -0
  99. package/src/commands/vydra.js +0 -0
  100. package/src/commands/web-fetch.js +0 -0
  101. package/src/commands/webhooks.js +0 -0
  102. package/src/commands/whatsapp.js +0 -0
  103. package/src/commands/wiki.js +0 -0
  104. package/src/commands/workboard.js +0 -0
  105. package/src/tools/audio_understanding.js +0 -0
  106. package/src/tools/bash.js +0 -0
  107. package/src/tools/browser.js +0 -0
  108. package/src/tools/canvas.js +0 -0
  109. package/src/tools/dashboard.js +2 -1
  110. package/src/tools/document_extract.js +0 -0
  111. package/src/tools/duckduckgo.js +0 -0
  112. package/src/tools/exa_search.js +0 -0
  113. package/src/tools/filesystem.js +0 -0
  114. package/src/tools/firecrawl.js +0 -0
  115. package/src/tools/git.js +0 -0
  116. package/src/tools/http.js +0 -0
  117. package/src/tools/image_generation.js +0 -0
  118. package/src/tools/list_dir.js +0 -0
  119. package/src/tools/llm_task.js +0 -0
  120. package/src/tools/media_understanding.js +0 -0
  121. package/src/tools/memory_write.js +70 -16
  122. package/src/tools/music_generation.js +0 -0
  123. package/src/tools/parallel_search.js +0 -0
  124. package/src/tools/phone_control.js +0 -0
  125. package/src/tools/phone_control_enhanced.js +0 -0
  126. package/src/tools/read_file.js +0 -0
  127. package/src/tools/searxng.js +0 -0
  128. package/src/tools/speech_to_text.js +0 -0
  129. package/src/tools/text_to_speech.js +0 -0
  130. package/src/tools/thread_ownership.js +0 -0
  131. package/src/tools/video_generation.js +0 -0
  132. package/src/tools/web_readability.js +0 -0
  133. package/src/tools/web_search.js +0 -0
  134. package/src/tools/write_file.js +0 -0
  135. package/src/utils/agents-md.js +0 -0
  136. package/src/utils/agents.js +0 -0
  137. package/src/utils/api.js +47 -40
  138. package/src/utils/approvals.js +27 -12
  139. package/src/utils/atomic-file.js +91 -0
  140. package/src/utils/background.js +0 -0
  141. package/src/utils/baileys.js +0 -0
  142. package/src/utils/commands.js +0 -0
  143. package/src/utils/config.js +0 -0
  144. package/src/utils/cron.js +0 -0
  145. package/src/utils/dashboard-server.js +3 -2
  146. package/src/utils/error.js +4 -0
  147. package/src/utils/errors.js +0 -0
  148. package/src/utils/format.js +0 -0
  149. package/src/utils/gateway-ws.js +0 -0
  150. package/src/utils/headless.js +0 -0
  151. package/src/utils/history.js +6 -14
  152. package/src/utils/hooks.js +0 -0
  153. package/src/utils/inquirer-wrapper.js +0 -0
  154. package/src/utils/mcp-client.js +0 -0
  155. package/src/utils/mcp.js +0 -0
  156. package/src/utils/memory.js +0 -0
  157. package/src/utils/parallel-tools.js +0 -0
  158. package/src/utils/path-utils.js +0 -0
  159. package/src/utils/plugin-registry.js +0 -0
  160. package/src/utils/ports.js +44 -0
  161. package/src/utils/process-errors.js +115 -0
  162. package/src/utils/provider-detect.js +78 -0
  163. package/src/utils/secrets.js +0 -0
  164. package/src/utils/sessions.js +6 -13
  165. package/src/utils/skills.js +0 -0
  166. package/src/utils/streaming-tools.js +97 -0
  167. package/src/utils/sub-agent.js +0 -0
  168. package/src/utils/token-budget.js +0 -0
  169. package/src/utils/tool-adapter.js +0 -0
  170. package/src/utils/tool-runner.js +0 -0
  171. package/src/utils/tools.js +4 -2
  172. package/src/utils/web-fetch.js +0 -0
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { CONFIG_DIR } = require('./config');
4
+ const { writeJsonAtomicSync, readJsonSafeSync } = require('./atomic-file');
4
5
 
5
6
  const HISTORY_DIR = path.join(CONFIG_DIR, 'history');
6
7
 
@@ -20,26 +21,17 @@ function getHistoryFilePath(botId) {
20
21
  // Load conversation history for a bot
21
22
  function loadHistory(botId) {
22
23
  const filePath = getHistoryFilePath(botId);
23
-
24
- if (!fs.existsSync(filePath)) {
25
- return [];
26
- }
27
-
28
- try {
29
- const content = fs.readFileSync(filePath, 'utf8');
30
- return JSON.parse(content);
31
- } catch {
32
- return [];
33
- }
24
+ const data = readJsonSafeSync(filePath, []);
25
+ // Normalize: older versions may have saved non-array; defensive cast.
26
+ return Array.isArray(data) ? data : [];
34
27
  }
35
28
 
36
29
  // Save conversation history for a bot
37
30
  function saveHistory(botId, history) {
38
31
  const filePath = getHistoryFilePath(botId);
39
-
40
32
  try {
41
- fs.writeFileSync(filePath, JSON.stringify(history, null, 2), 'utf8');
42
- } catch (err) {
33
+ writeJsonAtomicSync(filePath, history);
34
+ } catch {
43
35
  // Silently fail - history is not critical
44
36
  }
45
37
  }
File without changes
File without changes
File without changes
package/src/utils/mcp.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Centralized port + host defaults with env-var overrides.
3
+ *
4
+ * Hardcoding `7421` across multiple modules invited "port already in use"
5
+ * crashes in environments where the user runs natureco alongside other
6
+ * services on that port, or wants to run multiple natureco dashboards
7
+ * (e.g. one per project worktree). This module is the single source of
8
+ * truth — set NATURECO_DASHBOARD_PORT / NATURECO_DASHBOARD_HOST once and
9
+ * all callers pick it up.
10
+ */
11
+
12
+ const DEFAULT_DASHBOARD_PORT = 7421;
13
+ const DEFAULT_DASHBOARD_HOST = '127.0.0.1';
14
+
15
+ function _parsePort(raw, fallback) {
16
+ if (raw === undefined || raw === null || raw === '') return fallback;
17
+ const n = parseInt(String(raw).trim(), 10);
18
+ // Bind to a reserved port (≤1024) usually fails and is almost never
19
+ // what the user meant; clamp out-of-range values to the default.
20
+ if (!Number.isFinite(n) || n < 1 || n > 65535) return fallback;
21
+ return n;
22
+ }
23
+
24
+ function getDashboardPort() {
25
+ return _parsePort(process.env.NATURECO_DASHBOARD_PORT, DEFAULT_DASHBOARD_PORT);
26
+ }
27
+
28
+ function getDashboardHost() {
29
+ const raw = process.env.NATURECO_DASHBOARD_HOST;
30
+ return (raw && String(raw).trim()) || DEFAULT_DASHBOARD_HOST;
31
+ }
32
+
33
+ function getDashboardUrl() {
34
+ return `http://${getDashboardHost()}:${getDashboardPort()}`;
35
+ }
36
+
37
+ module.exports = {
38
+ DEFAULT_DASHBOARD_PORT,
39
+ DEFAULT_DASHBOARD_HOST,
40
+ getDashboardPort,
41
+ getDashboardHost,
42
+ getDashboardUrl,
43
+ _internals: { _parsePort },
44
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Top-level process error handlers.
3
+ *
4
+ * Node's default behavior for unhandled rejections is to print a deprecated
5
+ * warning and (since Node 15) exit with code 1. The default for uncaught
6
+ * exceptions is to print the stack and exit 1. Both bypass natureco's audit
7
+ * trail and dump raw Node output that's useless to a CLI user.
8
+ *
9
+ * `install({ audit, exit, stderr })` registers handlers that:
10
+ * 1. Append a structured entry to the audit log (synchronous — the
11
+ * process is about to die, async fire-and-forget would race),
12
+ * 2. Print a single friendly Turkish line + log path to stderr,
13
+ * 3. Exit with code 1 (configurable via `exit` for tests).
14
+ *
15
+ * Idempotent: a second call replaces the previous handlers (so the test
16
+ * suite can re-install with fresh spies).
17
+ */
18
+ const path = require('path');
19
+ const os = require('os');
20
+
21
+ const ERROR_LOG_PATH = path.join(os.homedir(), '.natureco', 'logs', 'crash.log');
22
+
23
+ let _installed = false;
24
+ let _registered = { rejection: null, exception: null, warning: null };
25
+
26
+ function _defaultAudit() {
27
+ try {
28
+ return require('./audit');
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ function _serializeError(err) {
35
+ if (!err) return { type: 'unknown', message: 'null' };
36
+ if (err instanceof Error) {
37
+ return {
38
+ type: err.constructor.name,
39
+ message: err.message,
40
+ stack: err.stack ? err.stack.split('\n').slice(0, 20).join('\n') : null,
41
+ code: err.code,
42
+ };
43
+ }
44
+ if (typeof err === 'object') {
45
+ try { return { type: 'object', message: JSON.stringify(err).slice(0, 1000) }; }
46
+ catch { return { type: 'object', message: String(err) }; }
47
+ }
48
+ return { type: typeof err, message: String(err) };
49
+ }
50
+
51
+ /**
52
+ * Install global handlers. Returns an `uninstall()` function.
53
+ *
54
+ * @param {{
55
+ * audit?: { logSync: Function, ACTIONS?: Record<string,string> } | null,
56
+ * exit?: (code: number) => void,
57
+ * stderr?: (msg: string) => void,
58
+ * }} [opts]
59
+ */
60
+ function install(opts = {}) {
61
+ const audit = opts.audit === undefined ? _defaultAudit() : opts.audit;
62
+ const exit = opts.exit || ((code) => process.exit(code));
63
+ const stderr = opts.stderr || ((msg) => process.stderr.write(msg));
64
+
65
+ // Replace any previous handlers (idempotency for tests).
66
+ if (_registered.rejection) process.off('unhandledRejection', _registered.rejection);
67
+ if (_registered.exception) process.off('uncaughtException', _registered.exception);
68
+
69
+ const onRejection = (reason) => {
70
+ const payload = { kind: 'unhandledRejection', error: _serializeError(reason) };
71
+ try { audit?.logSync('error', payload); } catch { /* ignore */ }
72
+ stderr(
73
+ `\n ✗ Beklenmedik bir hata oluştu (unhandled promise rejection).\n` +
74
+ ` Detay: ${payload.error.message}\n` +
75
+ ` Log: ${ERROR_LOG_PATH}\n` +
76
+ ` Hata raporu için: github.com/natureco-official/natureco-cli/issues\n\n`,
77
+ );
78
+ exit(1);
79
+ };
80
+
81
+ const onException = (err) => {
82
+ const payload = { kind: 'uncaughtException', error: _serializeError(err) };
83
+ try { audit?.logSync('error', payload); } catch { /* ignore */ }
84
+ stderr(
85
+ `\n ✗ Beklenmedik bir hata oluştu (uncaught exception).\n` +
86
+ ` Detay: ${payload.error.message}\n` +
87
+ ` Log: ${ERROR_LOG_PATH}\n` +
88
+ ` Hata raporu için: github.com/natureco-official/natureco-cli/issues\n\n`,
89
+ );
90
+ exit(1);
91
+ };
92
+
93
+ process.on('unhandledRejection', onRejection);
94
+ process.on('uncaughtException', onException);
95
+ _registered = { rejection: onRejection, exception: onException, warning: null };
96
+ _installed = true;
97
+
98
+ return function uninstall() {
99
+ if (_registered.rejection) process.off('unhandledRejection', _registered.rejection);
100
+ if (_registered.exception) process.off('uncaughtException', _registered.exception);
101
+ _registered = { rejection: null, exception: null, warning: null };
102
+ _installed = false;
103
+ };
104
+ }
105
+
106
+ function isInstalled() {
107
+ return _installed;
108
+ }
109
+
110
+ module.exports = {
111
+ install,
112
+ isInstalled,
113
+ ERROR_LOG_PATH,
114
+ _internals: { _serializeError },
115
+ };
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Provider detection — single source of truth for "given a base URL
3
+ * (and optionally a model name) which provider family is this?"
4
+ *
5
+ * Previously this logic lived inline in three places:
6
+ * - src/utils/api.js (the canonical detectProvider, exported only
7
+ * informally via its module surface)
8
+ * - src/commands/setup.js (string contains checks against minimax /
9
+ * anthropic / groq, scattered through the OAuth + test flows)
10
+ * - src/utils/api.js again, with `const isMiniMax = baseUrl.includes(
11
+ * 'minimax.io') || baseUrl.includes('minimaxi.com') || baseUrl.includes(
12
+ * 'minimax.cn')` repeated twice (lines 560 + 1017)
13
+ *
14
+ * Refactoring opportunity from the v5.7 audit. This module preserves
15
+ * EXACTLY the same detection rules as the original detectProvider so
16
+ * the migration is a behavioral no-op verifiable by tests.
17
+ */
18
+
19
+ /**
20
+ * Family of an inference provider.
21
+ *
22
+ * @typedef {'openai'|'anthropic'|'groq'|'openrouter'|'deepseek'|'mistral'
23
+ * |'together'|'fireworks'|'perplexity'|'ollama'|'minimax'} ProviderName
24
+ */
25
+
26
+ /**
27
+ * @param {string} providerUrl
28
+ * @param {string} [model]
29
+ * @returns {ProviderName}
30
+ */
31
+ function detectProvider(providerUrl, model) {
32
+ const url = (providerUrl || '').toLowerCase();
33
+ const m = (model || '').toLowerCase();
34
+ if (url.includes('anthropic.com') || m.includes('claude')) return 'anthropic';
35
+ if (url.includes('groq.com') || m.includes('groq') || m.includes('llama-3') || m.includes('mixtral')) return 'groq';
36
+ if (url.includes('openrouter.ai')) return 'openrouter';
37
+ if (url.includes('api.deepseek.com') || m.includes('deepseek')) return 'deepseek';
38
+ if (url.includes('mistral.ai') || m.includes('mistral') || m.includes('codestral')) return 'mistral';
39
+ if (url.includes('together.xyz') || m.includes('together')) return 'together';
40
+ if (url.includes('fireworks.ai') || m.includes('fireworks')) return 'fireworks';
41
+ if (url.includes('perplexity.ai') || m.includes('pplx') || m.includes('sonar')) return 'perplexity';
42
+ if (url.includes('localhost') || url.includes('127.0.0.1') || url.includes('ollama')) return 'ollama';
43
+ if (url.includes('minimax.io') || url.includes('minimax')) return 'minimax';
44
+ return 'openai';
45
+ }
46
+
47
+ /** Convenience predicates — each one short-circuits on hostname only. */
48
+ function isAnthropic(url) {
49
+ return (url || '').toLowerCase().includes('anthropic.com');
50
+ }
51
+
52
+ function isGroq(url) {
53
+ return (url || '').toLowerCase().includes('groq.com');
54
+ }
55
+
56
+ /**
57
+ * MiniMax has three known production hosts:
58
+ * api.minimax.io — global English-facing
59
+ * api.minimaxi.com — China-facing
60
+ * api.minimax.cn — legacy CN domain seen in some auth tokens
61
+ */
62
+ function isMiniMax(url) {
63
+ const u = (url || '').toLowerCase();
64
+ return u.includes('minimax.io') || u.includes('minimaxi.com') || u.includes('minimax.cn');
65
+ }
66
+
67
+ function isOllama(url) {
68
+ const u = (url || '').toLowerCase();
69
+ return u.includes('localhost') || u.includes('127.0.0.1') || u.includes('ollama');
70
+ }
71
+
72
+ module.exports = {
73
+ detectProvider,
74
+ isAnthropic,
75
+ isGroq,
76
+ isMiniMax,
77
+ isOllama,
78
+ };
File without changes
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
+ const { writeJsonAtomicSync, readJsonSafeSync } = require('./atomic-file');
4
5
 
5
6
  // ── Legacy botId-based sessions ─────────────────────────────────────────────────
6
7
 
@@ -36,20 +37,12 @@ function createSession(botId, botName) {
36
37
  function _saveBotSession(botId, session) {
37
38
  ensureSessionsDir(botId);
38
39
  const sessionFile = path.join(getSessionsDir(botId), `${session.id}.json`);
39
- fs.writeFileSync(sessionFile, JSON.stringify(session, null, 2), 'utf-8');
40
+ writeJsonAtomicSync(sessionFile, session);
40
41
  }
41
42
 
42
43
  function loadSession(botId, sessionId) {
43
44
  const sessionFile = path.join(getSessionsDir(botId), `${sessionId}.json`);
44
- if (!fs.existsSync(sessionFile)) {
45
- return null;
46
- }
47
- try {
48
- const data = fs.readFileSync(sessionFile, 'utf-8');
49
- return JSON.parse(data);
50
- } catch {
51
- return null;
52
- }
45
+ return readJsonSafeSync(sessionFile, null);
53
46
  }
54
47
 
55
48
  function getLatestSession(botId) {
@@ -110,10 +103,10 @@ function saveSession(commandName, messages, metadata = {}) {
110
103
  }
111
104
  const id = Date.now().toString(36);
112
105
  const filename = path.join(SESSIONS_DIR, `${commandName}-${id}.json`);
113
- fs.writeFileSync(filename, JSON.stringify({
106
+ writeJsonAtomicSync(filename, {
114
107
  id, commandName, messages, metadata,
115
- savedAt: new Date().toISOString()
116
- }, null, 2));
108
+ savedAt: new Date().toISOString(),
109
+ });
117
110
  return filename;
118
111
  }
119
112
 
File without changes
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Streaming tool-call delta accumulator.
3
+ *
4
+ * OpenAI-compatible providers stream tool calls as a sequence of partial
5
+ * `delta.tool_calls` chunks. Each chunk carries an `index`, optionally an
6
+ * `id`, and pieces of `function.name` / `function.arguments` that must be
7
+ * concatenated per index before the JSON arguments can be parsed.
8
+ *
9
+ * Broken (do-not-do) pattern, which crashes on multi-chunk arguments:
10
+ *
11
+ * const tc = delta.tool_calls[0];
12
+ * JSON.parse(tc.function.arguments); // SyntaxError on incomplete JSON
13
+ *
14
+ * Correct pattern (this module): keep a per-index buffer keyed on `index`,
15
+ * concat `name` and `arguments` as strings, JSON-parse only after `[DONE]`.
16
+ *
17
+ * See `~/.hermes/skills/llm-provider-integration/references/
18
+ * minimax-401-tool-calling-lessons.md` Lesson 2 for the failure-mode that
19
+ * motivated this. Both `src/utils/api.js` (non-REPL stream path) and
20
+ * `src/commands/repl.js` (REPL stream path) use this helper so the two
21
+ * implementations cannot drift.
22
+ */
23
+
24
+ /**
25
+ * Apply one streaming delta chunk to the per-index buffer.
26
+ * Mutates `buffer` in place and returns it for chaining.
27
+ *
28
+ * @param {Array<object>} buffer per-index accumulator (created externally,
29
+ * index = position; sparse entries fine)
30
+ * @param {Array<object>} deltas `parsed.choices[0].delta.tool_calls`
31
+ * @returns {Array<object>} the same buffer
32
+ */
33
+ function accumulateToolCallDeltas(buffer, deltas) {
34
+ if (!Array.isArray(deltas)) return buffer;
35
+ for (const tcDelta of deltas) {
36
+ if (!tcDelta || typeof tcDelta.index !== 'number') continue;
37
+ const idx = tcDelta.index;
38
+ if (!buffer[idx]) {
39
+ buffer[idx] = {
40
+ index: idx,
41
+ id: '',
42
+ type: 'function',
43
+ function: { name: '', arguments: '' },
44
+ };
45
+ }
46
+ const slot = buffer[idx];
47
+ if (tcDelta.id) slot.id = tcDelta.id;
48
+ if (tcDelta.type) slot.type = tcDelta.type;
49
+ if (tcDelta.function?.name) slot.function.name += tcDelta.function.name;
50
+ if (tcDelta.function?.arguments) slot.function.arguments += tcDelta.function.arguments;
51
+ }
52
+ return buffer;
53
+ }
54
+
55
+ /**
56
+ * Finalize accumulated buffer into the shape an OpenAI-compatible
57
+ * `assistant.tool_calls` array expects. Filters incomplete entries
58
+ * (missing name or id) and synthesizes ids when absent.
59
+ *
60
+ * @param {Array<object>} buffer from accumulateToolCallDeltas
61
+ * @returns {Array<{id:string,type:string,function:{name:string,arguments:string}}>}
62
+ */
63
+ function finalizeToolCalls(buffer) {
64
+ const out = [];
65
+ for (let i = 0; i < buffer.length; i++) {
66
+ const slot = buffer[i];
67
+ if (!slot || !slot.function.name) continue;
68
+ out.push({
69
+ id: slot.id || `call_${Date.now()}_${i}`,
70
+ type: slot.type || 'function',
71
+ function: { name: slot.function.name, arguments: slot.function.arguments || '' },
72
+ });
73
+ }
74
+ return out;
75
+ }
76
+
77
+ /**
78
+ * Parse the JSON arguments of a finalized tool call, returning {} on
79
+ * failure so callers don't have to repeat the try/catch.
80
+ *
81
+ * @param {string} argsJson
82
+ * @returns {object}
83
+ */
84
+ function parseToolArgs(argsJson) {
85
+ if (!argsJson || typeof argsJson !== 'string') return {};
86
+ try {
87
+ return JSON.parse(argsJson);
88
+ } catch {
89
+ return {};
90
+ }
91
+ }
92
+
93
+ module.exports = {
94
+ accumulateToolCallDeltas,
95
+ finalizeToolCalls,
96
+ parseToolArgs,
97
+ };
File without changes
File without changes
File without changes
File without changes
@@ -105,10 +105,12 @@ function toOpenAIFormat(toolDefs) {
105
105
  return toolDefs
106
106
  .filter(t => !['brave_search','brave-web-search','google_search','web_search','browse','open','search','shell','bash_command','execute_command','run_command','sql','query','lookup'].includes(t.name))
107
107
  .map(t => {
108
- // Alias varsa degistir
108
+ // Alias varsa degistir. Önceki kod ALIAS_MAP'i declare ediyordu ama
109
+ // değiştirmek için TOOL_ALIASES kullanıyordu (undefined) — alias hit
110
+ // olduğunda ReferenceError ile çöküyordu. ALIAS_MAP'e döndürüldü.
109
111
  const ALIAS_MAP = { 'brave_search':'duckduckgo_search','brave-web-search':'duckduckgo_search','google_search':'duckduckgo_search','web_search':'duckduckgo_search','browse':'browser','shell':'bash','bash_command':'bash','execute_command':'bash','run_command':'bash' };
110
112
  if (ALIAS_MAP[t.name]) {
111
- t = { ...t, name: TOOL_ALIASES[t.name] };
113
+ t = { ...t, name: ALIAS_MAP[t.name] };
112
114
  }
113
115
  // v5.4.21: Groq uyumluluk - additionalProperties: false kaldirildi
114
116
  // ve gereksiz kisitlamalar temizlendi
File without changes