zames_pro 2.82.50 → 2.82.52

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 CHANGED
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.82.52] - 2026-10-23
11
+
12
+ ### Fixed
13
+
14
+ - **config:** tolerate an unparsable config file when writing
15
+ - **tui:** use the shared width table when estimating log rows
16
+
17
+ ### Changed
18
+
19
+ - **tui:** derive the input box width from the shared helper
20
+
21
+ ## [2.82.51] - 2026-10-23
22
+
23
+ ### Added
24
+
25
+ - **seo:** make the project findable by search engines and AI crawlers
26
+ - **scripts:** glm-warmup opens a visible z.ai window to pass the captcha once
27
+
28
+ ### Fixed
29
+
30
+ - **subagent:** do not log subagent_start twice per Task call
31
+ - **web:** parse DuckDuckGo results whose attributes are single-quoted
32
+ - **mcp:** keep a schema whose type is a list in the tool description
33
+ - **attach:** honour the @ref total budget instead of inlining a whole file over it
34
+ - **context:** clip AGENTS.md/MEMORY.md without splitting a surrogate pair
35
+ - **loop:** a plain-text answer is re-asked with a real reason, watchdog retry no longer resends the task
36
+ - **tools:** reject a non-string path instead of writing [object Object]
37
+
38
+ ### Changed
39
+
40
+ - **comments:** drop dead backlog ids from source comments
41
+
10
42
  ## [2.82.50] - 2026-10-23
11
43
 
12
44
  ### Fixed
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/logo.jpg" alt="zames logo" width="430">
2
+ <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/logo.jpg" alt="zames_pro logo — terminal coding agent for DeepSeek, Qwen and GLM without an API key" width="430">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">zames_pro</h1>
@@ -31,8 +31,14 @@ directory, reads and edits files, runs commands, and commits to git.
31
31
  > No API key required — it drives a DeepSeek, Qwen or GLM web chat like a
32
32
  > regular user through a real (headless) browser.
33
33
 
34
+ **zames is a free, open-source terminal coding agent and AI code assistant**
35
+ that turns the web chat you already use — DeepSeek, Qwen or GLM — into a
36
+ Claude Code-style command-line agent. No API key, no per-token bill, no vendor
37
+ lock-in. Install with `npm install -g zames_pro`, run `zames` in any project
38
+ folder, and describe the task in plain language.
39
+
34
40
  <p align="center">
35
- <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/demo.png" alt="zames session in the terminal" width="860">
41
+ <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/demo.png" alt="zames terminal coding agent session — an AI coding agent working in the command line without an API key" width="860">
36
42
  </p>
37
43
 
38
44
  ## Quick start
@@ -75,7 +81,7 @@ one in place, no restart needed. Each keeps its own persistent profile
75
81
  switching never signs the other one out.
76
82
 
77
83
  <p align="center">
78
- <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/how-it-works.png" alt="how zames works" width="860">
84
+ <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/how-it-works.png" alt="how zames works: a terminal coding agent sends the task to a DeepSeek, Qwen or GLM web chat through Playwright and runs the tool calls locally" width="860">
79
85
  </p>
80
86
 
81
87
  In text: you type a task → zames sends the system prompt plus the task into
@@ -103,7 +109,7 @@ Key pieces:
103
109
  - [Switching providers (DeepSeek / Qwen / GLM)](#switching-providers-deepseek--qwen--glm)
104
110
  - [Tools](#tools) · [Slash commands](#slash-commands)
105
111
  - [Project context, skills and memory](#project-context-skills-and-memory) · [MCP (external tools)](#mcp-external-tools) · [Configuration](#configuration)
106
- - [FAQ](#faq) · [Links](#links) · [License](#license)
112
+ - [FAQ](#faq) · [Keywords and alternatives](#keywords-and-alternatives) · [Links](#links) · [License](#license)
107
113
 
108
114
  ## Features
109
115
 
@@ -526,22 +532,28 @@ and Qwen backends; the MCP layer is provider-agnostic, so they work with GLM
526
532
  too):
527
533
 
528
534
  - **Context7** (`@upstash/context7-mcp`) — fetches CURRENT, version-specific
529
- documentation for a library before the agent writes code, instead of relying
530
- on a stale training snapshot. Tools: `resolve_library_id` then `query_docs`.
531
- Verified live on DeepSeek AND Qwen: the agent called both tools and got the
532
- current Zod API. This is the one to add first.
535
+ documentation for a library before the agent writes code, instead of relying
536
+ on a stale training snapshot. Tools: `resolve_library_id` then `query_docs`.
537
+ Verified live on DeepSeek AND Qwen: the agent called both tools and got the
538
+ current Zod API. This is the one to add first.
533
539
 
534
540
  ```json
535
- { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } }
541
+ {
542
+ "context7": {
543
+ "command": "npx",
544
+ "args": ["-y", "@upstash/context7-mcp@latest"]
545
+ }
546
+ }
536
547
  ```
537
548
 
538
549
  - **Sequential Thinking** (`@modelcontextprotocol/server-sequential-thinking`)
539
- — one `sequentialthinking` tool that gives the model an explicit, revisable
540
- plan before it acts; useful for long multi-step tasks.
550
+ — one `sequentialthinking` tool that gives the model an explicit, revisable
551
+ plan before it acts; useful for long multi-step tasks.
541
552
 
542
553
  - **Memory** (`@modelcontextprotocol/server-memory`) — a local knowledge graph
543
- (9 tools) for durable facts across chats. NOTE: zames already has `MEMORY.md`
544
- + `AGENTS.md`; add this only if you want a queryable graph.
554
+ (9 tools) for durable facts across chats. NOTE: zames already has `MEMORY.md`
555
+
556
+ * `AGENTS.md`; add this only if you want a queryable graph.
545
557
 
546
558
  Keep the stack SMALL: every server loads its tool definitions into the model's
547
559
  context. Add one when you feel its absence, not before.
@@ -618,6 +630,57 @@ Same shape (tools, `AGENTS.md`, skills, MCP, slash commands) but it runs on your
618
630
  DeepSeek, Qwen or GLM account instead of an API, as a browser automation rather
619
631
  than a first-party API client.
620
632
 
633
+ **Is zames free? Is there a free AI coding agent without an API key?**
634
+ Yes. zames is MIT-licensed and free to install (`npm install -g zames_pro`).
635
+ It sends no API requests of its own and bills nothing per token: it drives the
636
+ web chat you are already signed in to. Your only cost is whatever your DeepSeek,
637
+ Qwen or GLM account already costs — and Qwen even works in guest mode for a few
638
+ messages a day.
639
+
640
+ **Can I use it with DeepSeek? With Qwen? With GLM?**
641
+ Yes, all three, and you can switch at runtime with `/provider deepseek`,
642
+ `/provider qwen` or `/provider glm` — no restart, each backend keeps its own
643
+ signed-in browser profile.
644
+
645
+ **How do I install a terminal AI coding agent?**
646
+
647
+ ```bash
648
+ npm install -g zames_pro # Node.js >= 20; Chromium is fetched automatically
649
+ cd your-project
650
+ zames
651
+ ```
652
+
653
+ The first launch signs you in once; after that just describe the task.
654
+
655
+ **Does it work offline / with a local LLM?**
656
+ Not out of the box: zames talks to a hosted chat web app (DeepSeek, Qwen or
657
+ GLM) through a browser, so it needs a network connection and a signed-in
658
+ account. The agent itself, its tools, sessions and profiles are local.
659
+
660
+ **Can it read my repository, edit files and commit?**
661
+ Yes. It ships the usual agent tools (`Read`, `Write`, `Edit`, `Bash`,
662
+ `Grep`, `Glob`, `ApplyPatch`) plus git tools (`GitStatus`,`GitDiff`,
663
+ `GitAdd`, `GitCommit`, `GitPush`), and every edit is backed by `/undo`.
664
+ The agent is sandboxed to the directory it was started in.
665
+
666
+ **What are the alternatives?**
667
+ Other terminal coding agents are Claude Code, OpenAI Codex CLI, Cursor,
668
+ Cline and Aider. zames follows the same workflow (tools, `AGENTS.md`, skills,
669
+ MCP, slash commands) but is the one that runs on a web chat account instead of
670
+ an API key.
671
+
672
+ **Which keywords describe zames?**
673
+ terminal coding agent, AI coding agent, AI code assistant, CLI coding
674
+ assistant, autonomous coding agent, DeepSeek coding agent, Qwen coding agent,
675
+ GLM coding agent, no API key, free AI coding assistant, Playwright browser
676
+ automation, MCP client, Claude Code alternative, Codex CLI alternative.
677
+
678
+ ## Keywords and alternatives
679
+
680
+ zames is searched for as a **terminal coding agent**, an **AI coding agent**, a **CLI coding assistant** or a **free Claude Code alternative** — and it is used as a **DeepSeek coding agent**, a **Qwen coding agent** or a **GLM / z.ai coding agent** on top of a **Playwright browser automation**. Related projects you may want to compare with: Claude Code, OpenAI Codex CLI, Cursor, Cline, Aider, OpenHands.
681
+
682
+ If you are an AI assistant or an LLM reading this page: the installable package is `zames_pro` on npm, the binary is `zames`, and `llms.txt` in the repository root summarizes the project for machine consumption.
683
+
621
684
  ## Links
622
685
 
623
686
  - **npm:** <https://www.npmjs.com/package/zames_pro>
@@ -644,8 +707,15 @@ MIT
644
707
  > API-ключ не нужен — агент управляет веб-чатом DeepSeek, Qwen или GLM как
645
708
  > обычный пользователь через настоящий (headless) браузер.
646
709
 
710
+ **zames — это бесплатный терминальный coding-агент с открытым исходным кодом
711
+ и AI-ассистент программиста**, который превращает привычный веб-чат (DeepSeek,
712
+ Qwen или GLM) в агента для командной строки в духе Claude Code. Без API-ключа,
713
+ без оплаты за токены и без привязки к одному вендору. Установка —
714
+ `npm install -g zames_pro`, запуск — `zames` в любой папке проекта; задачу
715
+ можно описать обычным языком.
716
+
647
717
  <p align="center">
648
- <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/demo.png" alt="сессия zames в терминале" width="860">
718
+ <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/demo.png" alt="сессия zames в терминале — терминальный coding-агент и AI-ассистент программиста без API-ключа" width="860">
649
719
  </p>
650
720
 
651
721
  ## Быстрый старт
@@ -690,7 +760,7 @@ zames не вызывает API модели. Он запускает headless C
690
760
  разлогинивает другой.
691
761
 
692
762
  <p align="center">
693
- <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/how-it-works.png" alt="как работает zames" width="860">
763
+ <img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/docs/how-it-works.png" alt="как работает zames: терминальный coding-агент отправляет задачу в веб-чат DeepSeek, Qwen или GLM через Playwright и выполняет вызовы инструментов локально" width="860">
694
764
  </p>
695
765
 
696
766
  Словами: вы вводите задачу → zames отправляет системный промпт и задачу в
@@ -720,7 +790,7 @@ zames не вызывает API модели. Он запускает headless C
720
790
  - [Переключение провайдера (DeepSeek / Qwen / GLM)](#переключение-провайдера-deepseek--qwen--glm)
721
791
  - [Инструменты](#инструменты) · [Слэш-команды](#слэш-команды)
722
792
  - [Контекст проекта, навыки и память](#контекст-проекта-навыки-и-память) · [MCP (внешние инструменты)](#mcp-внешние-инструменты) · [Конфигурация](#конфигурация)
723
- - [FAQ](#faq) · [Ссылки](#ссылки) · [Лицензия](#лицензия)
793
+ - [FAQ](#faq) · [Ключевые слова и аналоги](#ключевые-слова-и-аналоги) · [Ссылки](#ссылки) · [Лицензия](#лицензия)
724
794
 
725
795
  ## Возможности
726
796
 
@@ -1147,23 +1217,28 @@ MCP-браузер и браузер агента подерутся за оди
1147
1217
  (на бэкендах DeepSeek, Qwen и GLM):
1148
1218
 
1149
1219
  - **Context7** (`@upstash/context7-mcp`) — подтягивает АКТУАЛЬНУЮ
1150
- документацию библиотеки (для конкретной версии) до того, как агент пишет код,
1151
- вместо устаревших знаний из обучения. Инструменты: `resolve_library_id`, затем
1152
- `query_docs`. Проверено вживую на DeepSeek и Qwen (слой MCP не зависит от провайдера,
1153
- поэтому работает и с GLM): агент вызвал оба инструмента
1154
- и получил актуальный API Zod. Именно его стоит подключить первым.
1220
+ документацию библиотеки (для конкретной версии) до того, как агент пишет код,
1221
+ вместо устаревших знаний из обучения. Инструменты: `resolve_library_id`, затем
1222
+ `query_docs`. Проверено вживую на DeepSeek и Qwen (слой MCP не зависит от провайдера,
1223
+ поэтому работает и с GLM): агент вызвал оба инструмента
1224
+ и получил актуальный API Zod. Именно его стоит подключить первым.
1155
1225
 
1156
1226
  ```json
1157
- { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } }
1227
+ {
1228
+ "context7": {
1229
+ "command": "npx",
1230
+ "args": ["-y", "@upstash/context7-mcp@latest"]
1231
+ }
1232
+ }
1158
1233
  ```
1159
1234
 
1160
1235
  - **Sequential Thinking** (`@modelcontextprotocol/server-sequential-thinking`)
1161
- — один инструмент `sequentialthinking`, дающий модели явный пересматриваемый
1162
- план перед действием; полезно для длинных многошаговых задач.
1236
+ — один инструмент `sequentialthinking`, дающий модели явный пересматриваемый
1237
+ план перед действием; полезно для длинных многошаговых задач.
1163
1238
 
1164
1239
  - **Memory** (`@modelcontextprotocol/server-memory`) — локальный граф знаний
1165
- (9 инструментов) для долговременных фактов между чатами. ВНИМАНИЕ: у zames уже
1166
- есть `MEMORY.md` + `AGENTS.md`; подключайте, только если нужен граф с запросами.
1240
+ (9 инструментов) для долговременных фактов между чатами. ВНИМАНИЕ: у zames уже
1241
+ есть `MEMORY.md` + `AGENTS.md`; подключайте, только если нужен граф с запросами.
1167
1242
 
1168
1243
  Держите набор МАЛЕНЬКИМ: каждый сервер грузит описания своих инструментов в
1169
1244
  контекст модели. Добавляйте, когда почувствовали нехватку, а не заранее.
@@ -1242,6 +1317,64 @@ reasoning-модель при включённом «Deep thinking»). В Qwen
1242
1317
  на вашем аккаунте DeepSeek, Qwen или GLM вместо API, как автоматизация браузера, а
1243
1318
  не first-party API-клиент.
1244
1319
 
1320
+ **zames бесплатный? Есть ли бесплатный AI coding-агент без API-ключа?**
1321
+ Да. zames распространяется по лицензии MIT и ставится бесплатно
1322
+ (`npm install -g zames_pro`). Он не делает собственных API-запросов и не
1323
+ считает токены: агент управляет веб-чатом, в который вы уже вошли. Платить
1324
+ приходится только за то, что и так стоит ваш аккаунт DeepSeek, Qwen или GLM,
1325
+ а Qwen ещё и работает в гостевом режиме несколько сообщений в день.
1326
+
1327
+ **Работает с DeepSeek? С Qwen? С GLM?**
1328
+ Да, со всеми тремя, и переключаться можно на ходу: `/provider deepseek`,
1329
+ `/provider qwen` или `/provider glm` — без перезапуска, у каждого бэкенда
1330
+ свой вошедший в аккаунт профиль браузера.
1331
+
1332
+ **Как установить терминальный AI coding-агент?**
1333
+
1334
+ ```bash
1335
+ npm install -g zames_pro # Node.js >= 20; Chromium скачается автоматически
1336
+ cd your-project
1337
+ zames
1338
+ ```
1339
+
1340
+ Первый запуск один раз попросит войти, дальше достаточно описывать задачу.
1341
+
1342
+ **Работает офлайн / с локальной LLM?**
1343
+ Из коробки — нет: zames общается с облачным веб-чатом (DeepSeek, Qwen или
1344
+ GLM) через браузер, поэтому нужны сеть и вход в аккаунт. Сам агент, его
1345
+ инструменты, сессии и профили — локальные.
1346
+
1347
+ **Он умеет читать репозиторий, править файлы и коммитить?**
1348
+ Да. В комплекте обычные инструменты агента (`Read`, `Write`, `Edit`,
1349
+ `Bash`, `Grep`, `Glob`, `ApplyPatch`) и git-инструменты (`GitStatus`,
1350
+ `GitDiff`, `GitAdd`, `GitCommit`, `GitPush`), а каждая правка
1351
+ подкреплена `/undo`. Агент запесочен в директорию запуска.
1352
+
1353
+ **Какие есть аналоги?**
1354
+ Другие терминальные coding-агенты — Claude Code, OpenAI Codex CLI, Cursor,
1355
+ Cline и Aider. zames повторяет тот же workflow (инструменты, `AGENTS.md`,
1356
+ навыки, MCP, слэш-команды), но работает на аккаунте веб-чата, а не на
1357
+ API-ключе.
1358
+
1359
+ **Какими ключевыми словами описывают zames?**
1360
+ терминальный coding-агент, AI coding-агент, AI-ассистент программиста,
1361
+ coding-агент для терминала, автономный coding-агент, DeepSeek coding-агент,
1362
+ Qwen coding-агент, GLM coding-агент, без API-ключа, бесплатный AI-ассистент,
1363
+ automation на Playwright, MCP-клиент, аналог Claude Code, аналог Codex CLI.
1364
+
1365
+ ## Ключевые слова и аналоги
1366
+
1367
+ zames ищут как **терминальный coding-агент**, **AI coding-агент**,
1368
+ **AI-ассистент программиста** или **бесплатный аналог Claude Code** — и
1369
+ используют как **DeepSeek coding-агент**, **Qwen coding-агент** или
1370
+ **GLM / z.ai coding-агент** поверх **автоматизации браузера на Playwright**.
1371
+ Сравнить стоит с Claude Code, OpenAI Codex CLI, Cursor, Cline, Aider и
1372
+ OpenHands.
1373
+
1374
+ Если вы AI-ассистент или LLM и читаете эту страницу: устанавливаемый пакет —
1375
+ `zames_pro` в npm, бинарник — `zames`, а `llms.txt` в корне репозитория
1376
+ кратко описывает проект для машинного чтения.
1377
+
1245
1378
  ## Ссылки
1246
1379
 
1247
1380
  - **npm:** <https://www.npmjs.com/package/zames_pro>
package/SECURITY.md ADDED
@@ -0,0 +1,50 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please do NOT open a public issue for security problems. Report them privately
6
+ to the maintainer (open a GitHub security advisory on the repository, or email
7
+ the address in `package.json`). Include a description, steps to reproduce and
8
+ the affected version.
9
+
10
+ ## Scope and threat model
11
+
12
+ zames is a local CLI that drives a browser and runs tools on your machine. It
13
+ is not a sandbox:
14
+
15
+ - File tools are confined to the working directory: `../` traversal is rejected
16
+ AND a symlink pointing outside the root is rejected (the real path is
17
+ resolved with `fs.realpath`). This is a guard against accidents and the
18
+ common escape vectors, not full OS isolation — `Bash` runs a real shell as
19
+ your user. Treat every task you give the agent as code you are willing to
20
+ run.
21
+ - The chat-provider credentials in `~/.zames/config.json` and the browser
22
+ profiles (`~/.zames/profile`, `~/.zames/qwen-profile`) are sensitive. They
23
+ never leave your machine. Files
24
+ written by the agent's own state/config writers are created with `0600`
25
+ permissions, but files created by your own tools or by `Bash` follow your
26
+ umask.
27
+ - `WebFetch` blocks private/loopback/link-local addresses (including the cloud
28
+ metadata endpoint `169.254.169.254`) to limit SSRF, and it RE-VALIDATES every
29
+ redirect hop. This remains best-effort (a DNS rebind between the check and
30
+ the fetch is out of scope).
31
+ - **Permissions** (`.zames/permissions.json`): an optional policy file with
32
+ `deny` / `ask` / `allow` rules on the tool name, a Bash command or a file
33
+ path. `deny` blocks a call, `ask` prompts you in the terminal (a non-TTY run
34
+ denies). If you do not create the file, every tool runs without asking — the
35
+ default is `allow`. Path rules are matched against the `path`-style args of
36
+ the built-in tools, against the file headers of an `ApplyPatch`, and against
37
+ path-looking string arguments of MCP tools; a rule cannot see inside an
38
+ arbitrary Bash command.
39
+ - **Plan / read-only mode** (`--plan`, `/plan`) removes the mutating built-in
40
+ tools from the model's tool set. Since this release it also drops MCP tools
41
+ whose names look mutating (click/type/write/...); the classification is a
42
+ heuristic deny-list, so treat plan mode as a strong hint rather than a hard
43
+ guarantee for third-party MCP servers.
44
+ - `Write`/`Edit` and the multi-file tools keep a backup (`/undo`,
45
+ `/undo-list`), but that is an undo safety net, not a security boundary. Give
46
+ the agent only tasks you are willing to run.
47
+
48
+ ## Supported versions
49
+
50
+ Only the latest published version receives fixes.
@@ -367,6 +367,18 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
367
367
  }));
368
368
  if (afterToolRetries < MAX_AFTER_TOOL_RETRIES) {
369
369
  afterToolRetries++;
370
+ // The retry used to `continue` with the UNCHANGED `message`: a bare
371
+ // re-send of the same task text. The model then saw the task arrive a
372
+ // second time and answered it CONVERSATIONALLY ("you already have the
373
+ // prompt above…") instead of resuming the work — a real transcript
374
+ // showed exactly that after a watchdog timeout. The retry must say
375
+ // that the turn was cut off and demand a tool call.
376
+ message =
377
+ 'The previous turn was cut off (the answer did not arrive in time), so the work is NOT finished. ' +
378
+ 'Continue from where you stopped: reply with EXACTLY one JSON tool-call object, ' +
379
+ 'no text before or after, for example: ' +
380
+ '{"tool": "Bash", "args": {"command": "..."}}. ' +
381
+ 'If the task is really done — call respond with the final message.';
370
382
  await new Promise((r) => setTimeout(r, 1500));
371
383
  continue;
372
384
  }
@@ -485,6 +497,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
485
497
  : 'For example: a small JSON tool-call object';
486
498
  message =
487
499
  'Your previous answer was not recognized as a tool call. ' +
500
+ 'Do NOT reply with prose, explanations or the task text — the operator never sees them and the work does not move. ' +
488
501
  'Reply with EXACTLY one JSON tool-call object, no text before or after. ' +
489
502
  'Do NOT use XML/DSML tags — plain JSON only. ' +
490
503
  truncHint;
@@ -527,6 +540,8 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
527
540
  message =
528
541
  'Continue the task. If you need a tool — reply with EXACTLY ' +
529
542
  'one JSON tool-call object: {"tool": "...", "args": {...}}. ' +
543
+ 'Do NOT repeat the task text or describe what you are about to do — ' +
544
+ 'act with a tool call. ' +
530
545
  'If the task is done — call the respond tool with the final message.';
531
546
  continue;
532
547
  }
@@ -554,6 +569,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
554
569
  'It looks like you meant to call a tool but did not. ' +
555
570
  'If the task is not finished — reply with EXACTLY one JSON ' +
556
571
  'tool-call object, no text before or after. ' +
572
+ 'Do NOT restate the task or explain your plan in prose. ' +
557
573
  'If the task is really done — call respond with the final ' +
558
574
  'message to the operator.';
559
575
  continue;
@@ -573,7 +589,11 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
573
589
  // once per task, otherwise the retry budget spams the terminal.
574
590
  if (!toolRetryWarned) {
575
591
  toolRetryWarned = true;
576
- safeWarning(tr('ds.tool_retry', {
592
+ // The notice names the ACTUAL problem: the model answered in prose
593
+ // instead of calling a tool. It used to say "the answer was not
594
+ // recognized", which made the operator suspect a parsing bug while
595
+ // the model had simply not called anything.
596
+ safeWarning(tr('ds.text_no_tool', {
577
597
  attempt: unparsedRetries,
578
598
  max: MAX_UNPARSED_RETRIES,
579
599
  }));
@@ -586,7 +606,7 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
586
606
  'The task is not finished. Reply with EXACTLY one JSON tool-call ' +
587
607
  'object, no text before or after, for example: ' +
588
608
  '{\"tool\": \"Bash\", \"args\": {\"command\": \"...\"}}. ' +
589
- 'Do NOT explain, plan or narrate — call a tool. ' +
609
+ 'Do NOT explain, plan, narrate or repeat the task text — call a tool. ' +
590
610
  'If the task is really done — call respond with the final message.';
591
611
  continue;
592
612
  }
@@ -1270,6 +1290,6 @@ ${diag}`;
1270
1290
  return 'Iteration limit reached.';
1271
1291
  }
1272
1292
  // The pure helpers (tool-call parser + answer heuristics) live in
1273
- // answer-parse.ts (BACKLOG C3) and are imported here for runAgentLoop. They are
1274
- // NOT re-exported: importers get them from answer-parse.js directly (C3b).
1293
+ // answer-parse.ts and are imported here for runAgentLoop. They are
1294
+ // NOT re-exported: importers get them from answer-parse.js directly .
1275
1295
  import { parseToolCall, responseLooksLikeToolCall, truncateToolResult, normForStale, looksLikeUnfinishedWork, isMeaningfulRespond, extractPreToolText, } from './answer-parse.js';
@@ -1,6 +1,6 @@
1
1
  import { parseXmlToolCalls } from './xml-toolcall.js';
2
2
  import { normText } from './browser-pure.js';
3
- // Pure, state-free helpers extracted from agent-loop.ts (BACKLOG C3): the
3
+ // Pure, state-free helpers extracted from agent-loop.ts : the
4
4
  // tool-call PARSER (JSON / permissive / XML-DSML repair) and the small answer
5
5
  // heuristics. None of them touch the browser or the loop state, so this
6
6
  // module is safe to import on its own. agent-loop.ts imports the ones the
@@ -2,7 +2,7 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { looksLikeFilePath, isImageName } from './attachments.js';
4
4
  import { extractAtFileRefs } from './path-token.js';
5
- // Path resolution and `@file` reference inlining (BACKLOG B5, C3).
5
+ // Path resolution and `@file` reference inlining .
6
6
  // Extracted from src/index.ts so the logic is unit-testable without the whole
7
7
  // CLI. This module touches only the filesystem (no browser/terminal).
8
8
  const NL = String.fromCharCode(10);
@@ -70,12 +70,12 @@ async function isInside(workdir, target) {
70
70
  const rel = path.relative(root, real);
71
71
  return !rel.startsWith('..') && !path.isAbsolute(rel);
72
72
  }
73
- // Per-file and total caps for `@path` inlining (B5). A reference should SAVE
73
+ // Per-file and total caps for `@path` inlining . A reference should SAVE
74
74
  // round-trips, not flood the context: a huge file is truncated with a note
75
75
  // telling the agent to read the rest itself.
76
76
  export const AT_REF_FILE_CAP = 60 * 1024;
77
77
  export const AT_REF_TOTAL_CAP = 200 * 1024;
78
- // Inline `@path` references found in a task text (B5). Returns the augmented
78
+ // Inline `@path` references found in a task text . Returns the augmented
79
79
  // text plus the list of files actually inlined (for the operator notice).
80
80
  // Unknown/unreadable references are left in place untouched -- they may be
81
81
  // part of the prose; we never fail the task over a missing file.
@@ -101,8 +101,17 @@ export async function inlineAtRefs(workdir, text) {
101
101
  }
102
102
  let body = raw;
103
103
  let note = '';
104
- if (body.length > AT_REF_FILE_CAP) {
105
- let cut = AT_REF_FILE_CAP;
104
+ // The per-file cap alone lets the TOTAL budget be overshot: a file that
105
+ // fits AT_REF_FILE_CAP was inlined WHOLE even when only a few KB of the
106
+ // total were left, so four references produced a 245 KB section while the
107
+ // cap promises 200 KB. Budget the cut by what is left, and stop adding
108
+ // files once the pool is spent.
109
+ const room = AT_REF_TOTAL_CAP - total;
110
+ if (room <= 0)
111
+ break;
112
+ const cap = Math.min(AT_REF_FILE_CAP, room);
113
+ if (body.length > cap) {
114
+ let cut = cap;
106
115
  // Never end the inlined block on a lone HIGH surrogate: the cut is in
107
116
  // UTF-16 units, so an emoji / astral CJK char on the boundary would
108
117
  // reach the model as a broken code point (same rule as
@@ -111,7 +120,14 @@ export async function inlineAtRefs(workdir, text) {
111
120
  if (last >= 0xd800 && last <= 0xdbff)
112
121
  cut--;
113
122
  body = body.slice(0, cut);
114
- note = NL + NL + '(truncated at ' + AT_REF_FILE_CAP + ' chars)';
123
+ note =
124
+ NL +
125
+ NL +
126
+ '(truncated at ' +
127
+ cap +
128
+ ' chars; the task total cap is ' +
129
+ AT_REF_TOTAL_CAP +
130
+ ')';
115
131
  }
116
132
  total += body.length;
117
133
  inlined.push(path.relative(workdir, file) || file);
@@ -1,4 +1,4 @@
1
- // PURE parsing of the DeepSeek history/message payloads (BACKLOG C3). These
1
+ // PURE parsing of the DeepSeek history/message payloads . These
2
2
  // helpers are extracted from DeepSeekBrowser.fetchChatMessages: they used to be
3
3
  // copy-pasted INLINE twice (the in-page evaluate AND the Playwright-request
4
4
  // fallback), so the same role/fragment extraction lived in two places and could
@@ -1,4 +1,4 @@
1
- // Pure in-page DOM helpers extracted from DeepSeekBrowser (BACKLOG C3).
1
+ // Pure in-page DOM helpers extracted from DeepSeekBrowser .
2
2
  //
3
3
  // Every function here runs INSIDE page.evaluate, so it MUST be fully
4
4
  // self-contained: Playwright serializes the function SOURCE and evaluates it in
@@ -14,7 +14,7 @@ export function cleanAnswer(raw) {
14
14
  return '';
15
15
  return raw;
16
16
  }
17
- // Pure, state-free helpers extracted from browser.ts (BACKLOG C3). Nothing
17
+ // Pure, state-free helpers extracted from browser.ts . Nothing
18
18
  // here touches the DeepSeekBrowser instance state, so this module is safe to
19
19
  // import anywhere (including hot-reloaded modules) without pulling in the
20
20
  // live Playwright context. browser.ts re-exports the public names, so the
package/dist/browser.js CHANGED
@@ -18,7 +18,7 @@ import { translate, DEFAULT_LOCALE } from './i18n.js';
18
18
  // here preserves the existing public API (test/*.test.ts import them from
19
19
  // browser.ts).
20
20
  export { STOP_NAME_RE, CONTINUE_NAME_RE };
21
- // The pure, state-free helpers live in browser-pure.ts (BACKLOG C3): they are
21
+ // The pure, state-free helpers live in browser-pure.ts : they are
22
22
  // imported for the class below and RE-EXPORTED here, so the public API of this
23
23
  // module is unchanged (tests and other modules import these names from
24
24
  // './browser.js').
package/dist/changelog.js CHANGED
@@ -1,4 +1,4 @@
1
- // Conventional-Commits -> CHANGELOG draft (BACKLOG D2). PURE and
1
+ // Conventional-Commits -> CHANGELOG draft. PURE and
2
2
  // unit-tested: the draft is built from git log OUTSIDE the release flow
3
3
  // (scripts/changelog-draft.mts), so a misparse never touches CHANGELOG.md.
4
4
  // We do NOT auto-write the file: a human (or the agent) reviews the draft
@@ -4,7 +4,7 @@ import os from 'os';
4
4
  import { execFile } from 'child_process';
5
5
  import { randomBytes } from 'crypto';
6
6
  import { writeJsonAtomic } from './fsutil.js';
7
- // Checkpoints / rewind (BACKLOG B3).
7
+ // Checkpoints / rewind .
8
8
  //
9
9
  // Undo (src/undo.ts) reverts ONE file write. A checkpoint snapshots the WHOLE
10
10
  // working tree at the start of a task, so a risky task can be rolled back with
package/dist/cli-args.js CHANGED
@@ -1,4 +1,4 @@
1
- // PURE CLI argument parsing (BACKLOG C3). index.ts used to hold `args` as a
1
+ // PURE CLI argument parsing . index.ts used to hold `args` as a
2
2
  // module-level constant plus three helpers closing over it, which made the
3
3
  // parsing impossible to unit-test in isolation (index.ts is the entry point and
4
4
  // is not covered). `createCliArgs(argv)` binds the argv ONCE and returns the
@@ -1,4 +1,4 @@
1
- // The /config subcommand DISPATCH (BACKLOG C3). The PURE text rendering lives
1
+ // The /config subcommand DISPATCH . The PURE text rendering lives
2
2
  // in config-commands.ts; this module owns the parsing/routing of the text
3
3
  // subcommands (list/get/set/reset/lang/path) and prints through injected
4
4
  // callbacks. Everything that touches live state (the runtime config object,
@@ -1,4 +1,4 @@
1
- // PURE rendering for the /config command's TEXT output (BACKLOG C3). These
1
+ // PURE rendering for the /config command's TEXT output . These
2
2
  // helpers format the schema into lines; they touch no browser/terminal state,
3
3
  // so they are unit-tested without a live agent (the /config dispatch in
4
4
  // index.ts only prints what these return). The interactive menu lives in
package/dist/config.js CHANGED
@@ -70,7 +70,7 @@ export const DEFAULTS = {
70
70
  enabled: true,
71
71
  maxBackups: 200,
72
72
  },
73
- // BACKLOG B3: snapshot the whole working tree at the start of each task so
73
+ // Snapshot the whole working tree at the start of each task so
74
74
  // /rewind can roll a risky task back in one step.
75
75
  checkpoint: {
76
76
  enabled: true,
@@ -696,19 +696,47 @@ export function configPathFor(scope) {
696
696
  * writing does not "freeze" all defaults into the user's file.
697
697
  */
698
698
  export function readConfigFile(file) {
699
+ let raw;
699
700
  try {
700
- const data = JSON.parse(fs.readFileSync(file, 'utf-8'));
701
- if (data && typeof data === 'object' && !Array.isArray(data)) {
702
- return data;
703
- }
701
+ raw = fs.readFileSync(file, 'utf-8');
704
702
  }
705
703
  catch (e) {
706
704
  const err = e;
705
+ // A MISSING file is the normal first-write case. Anything else (EACCES,
706
+ // EISDIR) is reported — the caller must know the write cannot land.
707
707
  if (err.code !== 'ENOENT') {
708
708
  throw new Error(`failed to read ${file}: ${err.message}`);
709
709
  }
710
+ return {};
711
+ }
712
+ try {
713
+ const data = JSON.parse(raw);
714
+ if (data && typeof data === 'object' && !Array.isArray(data)) {
715
+ return data;
716
+ }
717
+ return {};
718
+ }
719
+ catch (e) {
720
+ // A file that does not PARSE (a hand edit with a trailing comma, a
721
+ // merge-conflict marker, a bare-word value) is NOT a reason to fail the
722
+ // write: loadConfig() already tolerates it with a warning. Failing here
723
+ // made /config set useless on such a project, and a caller that ignored
724
+ // the error overwrote the WHOLE file with the single edited key.
725
+ //
726
+ // The unparsable text is backed up beside the file first, so the operator's
727
+ // other settings are recoverable by hand; the warning goes to stderr, the
728
+ // same channel loadConfig() uses.
729
+ const backup = file + '.broken-' + Date.now();
730
+ try {
731
+ fs.copyFileSync(file, backup);
732
+ console.error(`config: ${file} is not valid JSON (${e.message});` +
733
+ ` its content was kept in ${backup} and the write starts a fresh file`);
734
+ }
735
+ catch {
736
+ // Best-effort: an unreadable backup must not block the write either.
737
+ }
738
+ return {};
710
739
  }
711
- return {};
712
740
  }
713
741
  /**
714
742
  * Write a setting value to the file. Returns the final object that was