zames_pro 1.1.0 → 1.2.0

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/README.md CHANGED
@@ -2,69 +2,59 @@
2
2
 
3
3
  ![zames logo](https://raw.githubusercontent.com/Viqto0r/zames_pro/master/logo.png)
4
4
 
5
- Terminal coding agent, работающий поверх chat.deepseek.com через Playwright.
6
- По духу похож на Claude Code / Codex CLI: запускается в текущей директории, читает и правит файлы, запускает команды, коммитит в git.
5
+ A terminal coding agent that works on top of [chat.deepseek.com](https://chat.deepseek.com/) through Playwright.
6
+ In spirit it is similar to Claude Code / Codex CLI: it starts in the current
7
+ directory, reads and edits files, runs commands, and commits to git.
7
8
 
8
- ## Требования
9
+ ## Requirements
9
10
 
10
11
  - Node.js >= 18
11
- - Google Chrome или Chromium (используется через Playwright)
12
- - Аккаунт DeepSeek (вход выполняется вручную в открывшемся браузере при первом запуске)
12
+ - Google Chrome or Chromium (used through Playwright)
13
+ - A DeepSeek account (you log in manually in the browser window that opens on first launch)
13
14
 
14
- ## Установка
15
+ ## Installation
15
16
 
16
- `bash
17
+ ```bash
17
18
  npm install -g zames_pro
18
19
  npx playwright install chromium
19
- `
20
+ ```
20
21
 
21
- ## Запуск
22
+ ## Usage
22
23
 
23
- Перейди в папку проекта и запусти:
24
+ Go to your project folder and run:
24
25
 
25
- `bash
26
+ ```bash
26
27
  zames
27
- `
28
+ ```
28
29
 
29
- Агент работает в директории запуска и не может выходить за её пределы (sandbox).
30
+ The agent works inside the directory it was started in and cannot leave it (sandbox).
30
31
 
31
- ### Опции
32
+ While the agent is working you can keep typing: press Enter to queue a message
33
+ (it is sent right after the current task, in the same chat), or Esc / Ctrl+C to
34
+ abort the current generation. This mirrors typing during generation on the
35
+ DeepSeek website.
36
+
37
+ ### Options
32
38
 
33
39
  ```
34
- zames --task <текст задачи>
40
+ zames --task <task text>
35
41
  zames --chat <id>
36
42
  zames --new-chat
37
43
  zames --resend-prompt
38
- zames --dir <путь>
44
+ zames --dir <path>
39
45
  zames --headless
40
46
  zames --debug
41
- zames --dev
42
47
  zames --version
43
48
  zames --help
44
49
  ```
45
50
 
46
- ## Конфигурация
47
-
48
- Глобальный конфиг: `~/.zames/config.json`
49
- Локальный (в проекте): `.zamesrc.json`
50
-
51
- Данные агента хранятся в `~/.zames`: профиль браузера, логи, история undo, снапшоты self-review.
52
-
53
- ## Разработка
54
-
55
- Для работы над самим агентом используй dev-режим:
56
-
57
- `bash
58
- npm run dev
59
- `
51
+ ## Configuration
60
52
 
61
- В этом режиме агент перед каждой задачей автоматически перечитывает модули логики (tools, agent-loop, system-prompt и др.) с диска — правки подхватываются без перезапуска браузера и без потери контекста чата. В обычном режиме (zames, npm start) авто-перечитывание выключено.
53
+ Global config: `~/.zames/config.json`
54
+ Local (per project): `.zamesrc.json`
62
55
 
63
- Служебные команды:
64
- - /reload — принудительно перечитать модули логики
65
- - /self-review — снять снапшот src/ и запустить самообзор
66
- - /status — состояние сессии
56
+ Agent data is stored in `~/.zames`: browser profile, logs, undo history, self-review snapshots.
67
57
 
68
- ## Лицензия и авторство
58
+ ## License
69
59
 
70
60
  MIT
package/logo.png CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agent-loop.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { buildSystemPrompt } from './system-prompt.js'
2
2
  import { getGitContext, formatGitContext } from './gitTools.js'
3
+ import { parseXmlToolCalls } from './xml-toolcall.js'
3
4
 
4
5
  export async function runAgentLoop({
5
6
  browser,
@@ -47,6 +48,12 @@ export async function runAgentLoop({
47
48
  let message = task
48
49
  transcript?.log('task', { task })
49
50
 
51
+ // Счётчик «ответ похож на tool-call, но не распознан». Чтобы модель,
52
+ // написавшая битый JSON/XML, не останавливала агента молча, мы просим
53
+ // её переотправить вызов. Ограничиваем число таких попыток.
54
+ let malformedRetries = 0
55
+ const MAX_MALFORMED_RETRIES = 3
56
+
50
57
  for (let i = 0; i < maxIterations; i++) {
51
58
  onThinking()
52
59
  const rawResponse = await browser.ask(message)
@@ -69,6 +76,23 @@ export async function runAgentLoop({
69
76
  }
70
77
 
71
78
  if (!parsed) {
79
+ // Ответ не распознан как tool-call. Если он ПОХОЖ на попытку вызова
80
+ // (есть tool/invoke/parameter, но JSON/XML битый) — это почти всегда
81
+ // ошибка формата. Не считаем её финальным ответом (иначе агент молча
82
+ // остановится), а просим модель переотправить вызов корректно.
83
+ const looksLikeToolCall = /("tool"\s*:|\binvoke\b|\bparameter\b)/i.test(rawResponse)
84
+ if (looksLikeToolCall && malformedRetries < MAX_MALFORMED_RETRIES) {
85
+ malformedRetries++
86
+ transcript?.log("malformed_toolcall", { attempt: malformedRetries, response: rawResponse })
87
+ if (debugLog) {
88
+ console.error("внимание: ответ похож на tool-call, но не распознан (попытка " + malformedRetries + "/" + MAX_MALFORMED_RETRIES + ")")
89
+ }
90
+ message = "Твой предыдущий ответ не распознан как вызов инструмента. " +
91
+ "Ответь РОВНО одним JSON-объектом вызова инструмента, без текста до и после. " +
92
+ "Например: " + String.fromCharCode(39) + "tool" + String.fromCharCode(39) + ": ..., args: {...}"
93
+ continue
94
+ }
95
+
72
96
  onAssistantMessage(rawResponse)
73
97
  transcript?.log('assistant_final', { message: rawResponse })
74
98
  return rawResponse
@@ -531,6 +555,10 @@ function parseToolCall(text) {
531
555
  if (tailPermissive) return { ...tailPermissive, _permissive: true }
532
556
  }
533
557
 
558
+ // Последний fallback: модель ответила XML/DSML-блоком вместо JSON.
559
+ const xmlCalls = parseXmlToolCalls(cleaned)
560
+ if (xmlCalls) return Array.isArray(xmlCalls) ? xmlCalls : [xmlCalls]
561
+
534
562
  return null
535
563
  }
536
564
 
package/src/browser.js CHANGED
@@ -3,6 +3,7 @@ import path from 'path'
3
3
  import os from 'os'
4
4
  import fs from 'fs/promises'
5
5
  import { execSync } from 'child_process'
6
+ import { theme } from './theme.js'
6
7
 
7
8
  const USER_DATA_DIR = path.join(os.homedir(), '.zames', 'profile')
8
9
  const CHAT_URL = 'https://chat.deepseek.com/'
@@ -78,6 +79,7 @@ export class DeepSeekBrowser {
78
79
  askRetries = 3,
79
80
  stabilityChecks = 3,
80
81
  stabilityDelayMs = 1000,
82
+ minSendIntervalMs = 25000,
81
83
  } = {}) {
82
84
  this.headless = headless
83
85
  this.debug = debug
@@ -86,6 +88,10 @@ export class DeepSeekBrowser {
86
88
  this.askRetries = askRetries
87
89
  this.stabilityChecks = stabilityChecks
88
90
  this.stabilityDelayMs = stabilityDelayMs
91
+ // Минимальный интервал между отправками в чат: DeepSeek ограничивает
92
+ // частоту («Messages too frequent. Try again later.»).
93
+ this.minSendIntervalMs = minSendIntervalMs
94
+ this._lastSentAt = 0
89
95
  this.context = null
90
96
  this.page = null
91
97
  }
@@ -418,6 +424,20 @@ export class DeepSeekBrowser {
418
424
  }
419
425
  }
420
426
 
427
+ // Ждём, пока с прошлой отправки пройдёт minSendIntervalMs. Защита от
428
+ // «Messages too frequent. Try again later.» на [chat.deepseek.com](https://chat.deepseek.com/).
429
+ async _waitForSendSlot() {
430
+ if (!this._lastSentAt) return // первая отправка — пауза не нужна
431
+ const gap = this.minSendIntervalMs - (Date.now() - this._lastSentAt)
432
+ if (gap <= 0) return
433
+ console.error(
434
+ theme.warn(
435
+ `⏳ пауза ${Math.ceil(gap / 1000)}с перед отправкой (лимит частоты DeepSeek)...`,
436
+ ),
437
+ )
438
+ await this.page.waitForTimeout(gap)
439
+ }
440
+
421
441
  async _askOnce(prompt, { timeout }) {
422
442
  const input = await this._findVisible(INPUT_SELECTORS, 10_000)
423
443
  if (!input) {
@@ -428,6 +448,7 @@ export class DeepSeekBrowser {
428
448
 
429
449
  const beforeText = await this._readLastAnswerTextClean().catch(() => '')
430
450
 
451
+ await this._waitForSendSlot()
431
452
  await this._setInputText(input, prompt)
432
453
  await this.page.waitForTimeout(200)
433
454
 
@@ -440,6 +461,7 @@ export class DeepSeekBrowser {
440
461
  } catch {
441
462
  await this.page.keyboard.press('Enter')
442
463
  }
464
+ this._lastSentAt = Date.now()
443
465
 
444
466
  const startDeadline = Date.now() + 15_000
445
467
  let started = false
package/src/config.js CHANGED
@@ -43,6 +43,7 @@ const DEFAULTS = {
43
43
  askRetries: 3,
44
44
  stabilityChecks: 3,
45
45
  stabilityDelayMs: 1000,
46
+ minSendIntervalMs: 25000,
46
47
  },
47
48
  }
48
49