zames_pro 2.10.0 → 2.10.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/dist/agent-loop.js +36 -13
- package/package.json +1 -1
package/dist/agent-loop.js
CHANGED
|
@@ -3,6 +3,27 @@ import { getGitContext, formatGitContext } from './gitTools.js';
|
|
|
3
3
|
import { parseXmlToolCalls } from './xml-toolcall.js';
|
|
4
4
|
import { translate } from './i18n.js';
|
|
5
5
|
export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 40, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, locale = 'ru', }) {
|
|
6
|
+
// UI callbacks must NEVER break the agent loop. A rendering error (a huge
|
|
7
|
+
// tool result, a broken markdown frame, a closed terminal) used to throw
|
|
8
|
+
// out of the loop right after a tool call — the session looked "stopped
|
|
9
|
+
// after a tool call", with a tool_call but no tool_result in the log. We
|
|
10
|
+
// wrap every callback so a UI failure is swallowed and the loop continues.
|
|
11
|
+
const safe = (fn) => {
|
|
12
|
+
return (...a) => {
|
|
13
|
+
try {
|
|
14
|
+
fn(...a);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Intentionally ignored: the loop must survive UI failures.
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
const safeThinking = safe(onThinking);
|
|
22
|
+
const safeAssistantThought = safe(onAssistantThought);
|
|
23
|
+
const safeToolCall = safe(onToolCall);
|
|
24
|
+
const safeToolResult = safe(onToolResult);
|
|
25
|
+
const safeAssistantMessage = safe(onAssistantMessage);
|
|
26
|
+
const safeWarning = safe(onWarning);
|
|
6
27
|
if (freshChat) {
|
|
7
28
|
await browser.newChat();
|
|
8
29
|
transcript?.log('new_chat');
|
|
@@ -42,7 +63,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
42
63
|
length: systemPrompt.length,
|
|
43
64
|
gitContext: gitText,
|
|
44
65
|
});
|
|
45
|
-
|
|
66
|
+
safeThinking();
|
|
46
67
|
// system-prompt is an agent send: throttled (agent: true).
|
|
47
68
|
await browser.ask(systemPrompt, { timeout: 60_000, agent: true });
|
|
48
69
|
await reportChat();
|
|
@@ -104,7 +125,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
104
125
|
let afterToolRetries = 0;
|
|
105
126
|
const MAX_AFTER_TOOL_RETRIES = 6;
|
|
106
127
|
for (let i = 0; i < maxIterations; i++) {
|
|
107
|
-
|
|
128
|
+
safeThinking();
|
|
108
129
|
// The first message (task) is user input: no throttle.
|
|
109
130
|
// Subsequent ones (tool-result and resend requests) are agent sends:
|
|
110
131
|
// throttled so we don't hit the rate limit.
|
|
@@ -139,7 +160,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
139
160
|
attempt: afterToolRetries,
|
|
140
161
|
error: e.message,
|
|
141
162
|
});
|
|
142
|
-
|
|
163
|
+
safeWarning('browser.ask() не вернул ответ за ' +
|
|
143
164
|
Math.round(askDeadlineMs / 1000) +
|
|
144
165
|
'с — повторяю запрос.');
|
|
145
166
|
if (afterToolRetries < MAX_AFTER_TOOL_RETRIES) {
|
|
@@ -150,7 +171,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
150
171
|
transcript?.log('ask_timeout_exhausted', {
|
|
151
172
|
message: 'ask() не вернул ответ и лимит повторов исчерпан',
|
|
152
173
|
});
|
|
153
|
-
|
|
174
|
+
safeWarning('browser.ask() перестал отвечать; лимит повторов исчерпан, ' +
|
|
154
175
|
'останавливаюсь. Ответа модели нет — проверьте чат DeepSeek вручную.');
|
|
155
176
|
return 'ask() watchdog: ответ модели не получен';
|
|
156
177
|
}
|
|
@@ -205,7 +226,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
205
226
|
if (parsed) {
|
|
206
227
|
const thought = extractPreToolText(rawResponse);
|
|
207
228
|
if (thought)
|
|
208
|
-
|
|
229
|
+
safeAssistantThought(thought);
|
|
209
230
|
}
|
|
210
231
|
const parsedCalls = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];
|
|
211
232
|
if (parsedCalls.some((p) => p && p._permissive)) {
|
|
@@ -334,12 +355,14 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
334
355
|
continue;
|
|
335
356
|
}
|
|
336
357
|
const suspiciousFinal = responseLooksLikeToolCall(rawResponse) ||
|
|
358
|
+
looksLikeUnfinishedWork((rawResponse || '').trim()) ||
|
|
337
359
|
!(rawResponse || '').trim();
|
|
338
360
|
if (suspiciousFinal) {
|
|
339
|
-
// The answer LOOKS like a call
|
|
340
|
-
// even after all retries: warn the
|
|
361
|
+
// The answer LOOKS like a call / promises work / is empty, but could
|
|
362
|
+
// not be turned into a tool call even after all retries: warn the
|
|
363
|
+
// operator instead of silently printing e.g. "Stale. Let me verify".
|
|
341
364
|
transcript?.log('suspicious_final', { response: rawResponse });
|
|
342
|
-
|
|
365
|
+
safeWarning(translate(locale)('msg.suspicious_stop'));
|
|
343
366
|
}
|
|
344
367
|
// A meaningful plain-text answer (e.g. a final report the model forgot to
|
|
345
368
|
// wrap in respond) is surfaced as-is WITHOUT a warning: after the bounded
|
|
@@ -391,11 +414,11 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
391
414
|
// the task normally.
|
|
392
415
|
if (!isMeaningfulRespond(msg)) {
|
|
393
416
|
transcript?.log('empty_respond_exhausted', { response: rawResponse });
|
|
394
|
-
|
|
417
|
+
safeWarning('Модель вызвала respond без текста, и лимит повторов исчерпан. ' +
|
|
395
418
|
'Проверьте чат DeepSeek вручную.');
|
|
396
419
|
return 'Модель не сформировала итоговое сообщение (пустой respond).';
|
|
397
420
|
}
|
|
398
|
-
|
|
421
|
+
safeAssistantMessage(msg);
|
|
399
422
|
transcript?.log('assistant_final', { message: msg });
|
|
400
423
|
return msg;
|
|
401
424
|
}
|
|
@@ -408,12 +431,12 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
408
431
|
const tool = tools.find((t) => t.name === call.tool);
|
|
409
432
|
if (!tool) {
|
|
410
433
|
const err = `Неизвестный инструмент: ${call.tool}`;
|
|
411
|
-
|
|
434
|
+
safeToolResult(err);
|
|
412
435
|
transcript?.log('tool_error', { tool: call.tool, error: err });
|
|
413
436
|
results.push({ tool: call.tool, result: err });
|
|
414
437
|
continue;
|
|
415
438
|
}
|
|
416
|
-
|
|
439
|
+
safeToolCall(call.tool, call.args);
|
|
417
440
|
transcript?.log('tool_call', { tool: call.tool, args: call.args });
|
|
418
441
|
let result;
|
|
419
442
|
try {
|
|
@@ -422,7 +445,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
422
445
|
catch (e) {
|
|
423
446
|
result = `Ошибка: ${e.message}`;
|
|
424
447
|
}
|
|
425
|
-
|
|
448
|
+
safeToolResult(result);
|
|
426
449
|
transcript?.log('tool_result', {
|
|
427
450
|
tool: call.tool,
|
|
428
451
|
result: String(result),
|