zames_pro 2.15.4 → 2.15.5

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.
@@ -4,7 +4,7 @@ import { getGitContext, formatGitContext } from './gitTools.js';
4
4
  import { parseXmlToolCalls } from './xml-toolcall.js';
5
5
  import { translate } from './i18n.js';
6
6
  import { normText } from './browser.js';
7
- export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 200, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, locale = 'ru', }) {
7
+ export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 0, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, locale = 'ru', }) {
8
8
  // UI callbacks must NEVER break the agent loop. A rendering error (a huge
9
9
  // tool result, a broken markdown frame, a closed terminal) used to throw
10
10
  // out of the loop right after a tool call — the session looked "stopped
@@ -153,8 +153,14 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
153
153
  // not come back in time. This is separate from unparsedRetries because a
154
154
  // timeout is an infrastructure failure, not a model protocol violation.
155
155
  let afterToolRetries = 0;
156
+ // toolsRanInTask: how many tools actually ran in THIS task. The protocol
157
+ // guard uses it to tell "started, then slipped into chat mode" (a reasoning
158
+ // paragraph that is neither a tool call nor a real respond) from a genuine
159
+ // short answer. We key on the STRUCTURE (work already started), not words.
160
+ let toolsRanInTask = 0;
156
161
  const MAX_AFTER_TOOL_RETRIES = 6;
157
- for (let i = 0; i < maxIterations; i++) {
162
+ const iterCap = maxIterations > 0 ? maxIterations : 100000;
163
+ for (let i = 0; i < iterCap; i++) {
158
164
  // NOTE: the spinner is NOT started here. browser.onSendStart fires it
159
165
  // right when the message is actually typed/sent (after the send-pause),
160
166
  // so no spinner runs during the pre-send phase.
@@ -416,6 +422,26 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
416
422
  '{\"tool\": \"respond\", \"args\": {\"message\": \"...\"}}';
417
423
  continue;
418
424
  }
425
+ // STRUCTURAL guard against "the model slipped into chat mode":
426
+ // after work has already started (a tool really ran in this task), a
427
+ // plain-text answer that is neither a tool call nor a real respond must
428
+ // NOT be returned to the operator as the final result. Returning it is
429
+ // exactly the "agent stopped mid-task" symptom (e.g. it writes a reasoning
430
+ // paragraph "Now let me analyze..." instead of calling a tool). We do not
431
+ // match words here - the STRUCTURE (toolsRanInTask > 0) is the signal.
432
+ if (toolsRanInTask > 0) {
433
+ transcript?.log('protocol_violation_final', {
434
+ response: rawResponse.slice(0, 500),
435
+ });
436
+ safeWarning(translate(locale)('msg.suspicious_stop'));
437
+ // The model stopped calling tools mid-task. Surface the last text as
438
+ // a READABLE report, but say explicitly that the task may be incomplete:
439
+ // never let a reasoning paragraph masquerade as a finished result.
440
+ const lastText = (rawResponse || '').trim();
441
+ return (lastText
442
+ ? lastText + String.fromCharCode(10) + String.fromCharCode(10) + '(The model stopped calling tools before finishing. The task may be incomplete - check the DeepSeek chat.)'
443
+ : 'The model stopped calling tools before finishing the task.');
444
+ }
419
445
  const suspiciousFinal = responseLooksLikeToolCall(rawResponse) ||
420
446
  looksLikeUnfinishedWork((rawResponse || '').trim()) ||
421
447
  !(rawResponse || '').trim();
@@ -517,6 +543,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
517
543
  // A tool just ran — the next answer is expected to be a fresh tool call.
518
544
  // Reset the watchdog so the next empty/repeated answer is nudged.
519
545
  justRanTool = true;
546
+ toolsRanInTask++;
520
547
  // The tool really executed on this turn. The NEXT iteration will treat
521
548
  // a repeat of this answer as stale only because of this flag (see NEXT,
522
549
  // not the current, justRanTool check).
package/dist/config.js CHANGED
@@ -6,7 +6,7 @@ const ZAMES_HOME = path.join(os.homedir(), '.zames');
6
6
  const HOME_CONFIG = path.join(ZAMES_HOME, 'config.json');
7
7
  const PROJECT_CONFIG = path.join(process.cwd(), '.zamesrc.json');
8
8
  export const DEFAULTS = {
9
- maxIterations: 200,
9
+ maxIterations: 0,
10
10
  headless: false,
11
11
  debug: false,
12
12
  browserChannel: null,
@@ -77,7 +77,7 @@ function deepMerge(target, source) {
77
77
  }
78
78
  export const CONFIG_SCHEMA = [
79
79
  { path: 'ui.locale', type: 'enum', values: ['ru', 'en'], labelKey: 'cfg.f.ui_locale', groupKey: 'cfg.group.ui' },
80
- { path: 'maxIterations', type: 'number', min: 1, max: 100000, labelKey: 'cfg.f.maxIterations', groupKey: 'cfg.group.agent' },
80
+ { path: 'maxIterations', type: 'number', min: 0, max: 100000, labelKey: 'cfg.f.maxIterations', groupKey: 'cfg.group.agent' },
81
81
  { path: 'headless', type: 'boolean', labelKey: 'cfg.f.headless', groupKey: 'cfg.group.agent' },
82
82
  { path: 'debug', type: 'boolean', labelKey: 'cfg.f.debug', groupKey: 'cfg.group.agent' },
83
83
  { path: 'hotReload', type: 'boolean', labelKey: 'cfg.f.hotReload', groupKey: 'cfg.group.agent' },
package/dist/i18n.js CHANGED
@@ -45,7 +45,7 @@ const CATALOG = {
45
45
  'help.opt.resume_last': { ru: 'вернуться в последний сохранённый чат', en: 'resume the last saved chat' },
46
46
  'help.opt.new_chat': { ru: 'начать новый чат (по умолчанию)', en: 'start a new chat (default)' },
47
47
  'help.opt.resend_prompt': { ru: 'дослать system-prompt в существующий чат', en: 'resend system-prompt into an existing chat' },
48
- 'help.opt.max_iter': { ru: 'лимит итераций (по умолчанию {n})', en: 'iteration limit (default {n})' },
48
+ 'help.opt.max_iter': { ru: 'лимит итераций, 0 = без лимита ({n})', en: 'iteration limit, 0 = unlimited ({n})' },
49
49
  'help.opt.headless': { ru: 'браузер без UI', en: 'headless browser' },
50
50
  'help.opt.debug': { ru: 'подробный лог', en: 'verbose log' },
51
51
  'help.opt.calibrate': { ru: 'режим калибровки селекторов', en: 'selector calibration mode' },
@@ -144,7 +144,7 @@ const CATALOG = {
144
144
  'status.last_chat': { ru: 'Last chat: {v}', en: 'Last chat: {v}' },
145
145
  'status.sessions': { ru: 'Сессии: {v}', en: 'Sessions: {v}' },
146
146
  'status.dev': { ru: 'Dev mode (auto-reload): {v}', en: 'Dev mode (auto-reload): {v}' },
147
- 'status.max_iter': { ru: 'Лимит итераций: {v}', en: 'Iteration limit: {v}' },
147
+ 'status.max_iter': { ru: 'Лимит итераций: {v} (0 = без лимита)', en: 'Iteration limit: {v} (0 = unlimited)' },
148
148
  'status.headless': { ru: 'Headless: {v}', en: 'Headless: {v}' },
149
149
  'status.debug': { ru: 'Debug: {v}', en: 'Debug: {v}' },
150
150
  'status.undo': { ru: 'Undo: {v}', en: 'Undo: {v}' },
package/dist/index.js CHANGED
@@ -53,8 +53,8 @@ const t = (key, params) => translate(currentLocale)(key, params);
53
53
  const headless = hasFlag('--headless') || config.headless;
54
54
  const debug = hasFlag('--debug') || config.debug;
55
55
  const calibrate = hasFlag('--calibrate');
56
- const maxIter = Number(getArg('--max-iter', String(config.maxIterations))) ||
57
- config.maxIterations;
56
+ const maxIterArg = getArg('--max-iter', null);
57
+ const maxIter = maxIterArg !== null ? Number(maxIterArg) : config.maxIterations;
58
58
  const positional = getPositional();
59
59
  const task = getArg('--task', positional.join(' ').trim() || null);
60
60
  const chatIdArg = getArg('--chat', null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.15.4",
3
+ "version": "2.15.5",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {