zames_pro 2.66.1 → 2.67.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.
- package/CHANGELOG.md +192 -129
- package/README.md +2 -1
- package/dist/attachments.js +0 -17
- package/dist/context.js +0 -3
- package/dist/extraTools.js +10 -11
- package/dist/fsutil.js +38 -1
- package/dist/gitTools.js +83 -26
- package/dist/index.js +18 -18
- package/dist/input.js +134 -30
- package/dist/mcp.js +49 -10
- package/dist/permissions.js +40 -1
- package/dist/run-task.js +6 -0
- package/dist/sandbox.js +49 -0
- package/dist/scheduler.js +8 -1
- package/dist/self-review.js +37 -17
- package/dist/shell.js +74 -32
- package/dist/tools.js +40 -20
- package/dist/transcript.js +15 -2
- package/dist/web.js +107 -38
- package/package.json +1 -2
- package/scripts/build-readme.mts +30 -51
- package/scripts/coverage-gate.mjs +4 -0
- package/README.ru.md +0 -971
package/CHANGELOG.md
CHANGED
|
@@ -7,16 +7,98 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.67.1] - 2026-10-08
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **web:** the headless renderer reuses ONE BrowserContext for every
|
|
15
|
+
`WebFetch` with `render=true` (it used to build a fresh context per call);
|
|
16
|
+
the page is closed after each fetch and the context in `closeWeb()`.
|
|
17
|
+
- **docs:** `npm run self-smoke` is now documented as a REQUIRED manual
|
|
18
|
+
pre-release step in DEVELOPING.md — it is the only gate that catches an
|
|
19
|
+
integration regression (login, send, attachments) and needs live creds,
|
|
20
|
+
so it stays out of CI.
|
|
21
|
+
- **packaging:** `README.ru.md` is no longer generated or published. The
|
|
22
|
+
Russian translation already lives inside README.md as a `<details>` block
|
|
23
|
+
(visible on npm), and the language switcher is now an in-page `#readme-ru`
|
|
24
|
+
anchor — the second ~15 KB file only duplicated it and could drift.
|
|
25
|
+
|
|
26
|
+
## [2.67.0] - 2026-10-08
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **tools:** Edit/MultiEdit no longer corrupt dollar patterns (`$&`, `$$`, `$1`)
|
|
31
|
+
in `new_string` — the replacement now uses the functional form instead of a
|
|
32
|
+
string replacer.
|
|
33
|
+
- **tools:** Grep and findstr are run through `execFile` with an argument
|
|
34
|
+
array instead of a shell string, so a `$(...)` pattern is no longer executed
|
|
35
|
+
as a command.
|
|
36
|
+
- **git:** GitShow/GitDiff/GitAdd/GitPush/GitCommit now use `execFile`; `ref`
|
|
37
|
+
and `path` are validated (a leading `-` is rejected) — command/option
|
|
38
|
+
injection through the arguments of read-only tools is closed.
|
|
39
|
+
- **sandbox:** file tools resolve the real path (`fs.realpath`) and reject a
|
|
40
|
+
symlink pointing outside the working directory.
|
|
41
|
+
- **shell:** `assertCommandInsideRoot` expands `~`/`$HOME`, sees a `cd` inside
|
|
42
|
+
a subshell/backticks and rejects `eval` with `cd`.
|
|
43
|
+
- **plan mode:** MCP tools are now filtered for read-only too (mutating verbs
|
|
44
|
+
are dropped).
|
|
45
|
+
- **self-review:** the snapshot and diff/apply walk src/ subdirectories
|
|
46
|
+
recursively (`i18n/`, `input/`) — they used to be silently missing from the
|
|
47
|
+
snapshot.
|
|
48
|
+
- **one-shot `--task`:** on an agent error / iteration limit / watchdog the
|
|
49
|
+
process now exits with code 1 instead of 0.
|
|
50
|
+
- **input:** Ctrl+Delete and Delete now work under the CSI-u (kitty) protocol:
|
|
51
|
+
`CSI 3;5u`/`3;3u`/`3u`.
|
|
52
|
+
- **input:** Up/Down move the cursor across VISUAL rows of a wrapped line, not
|
|
53
|
+
only across logical lines.
|
|
54
|
+
- **input:** a lone ESC no longer aborts the generation when a control
|
|
55
|
+
sequence arrives in two chunks — a short timeout disambiguation.
|
|
56
|
+
- **input:** Ctrl+L clears the screen and PageUp/PageDown page the suggestion
|
|
57
|
+
list; an unknown control sequence is logged in debug instead of being
|
|
58
|
+
swallowed silently.
|
|
59
|
+
- **MCP:** duplicate tool names are disambiguated with a numeric suffix so the
|
|
60
|
+
second tool is not silently unreachable.
|
|
61
|
+
- **MCP:** a tool result is truncated with an explicit marker — a single call
|
|
62
|
+
(browser_snapshot and the like) no longer floods the context.
|
|
63
|
+
- **transcript:** `close()` waits for the flush (it returns a promise) so the
|
|
64
|
+
final events reach the file on the exit paths.
|
|
65
|
+
- **web:** `WebFetch` re-validates every redirect hop against the SSRF guard
|
|
66
|
+
and checks the final URL of a headless render.
|
|
67
|
+
- **permissions:** a `path` rule now also matches the file headers of an
|
|
68
|
+
`ApplyPatch` and path-looking arguments of MCP tools.
|
|
69
|
+
- **scheduler:** `nextCronTime` parses the cron expression once instead of on
|
|
70
|
+
every scanned minute.
|
|
71
|
+
- **git:** `formatGitContext` picks a code fence longer than any backtick run
|
|
72
|
+
in the preview, so a filename with backticks cannot break the markdown.
|
|
73
|
+
- **fsutil:** secrets and state are written with 0600 permissions;
|
|
74
|
+
`writeFileAtomic` fsyncs the file before rename (and the directory after)
|
|
75
|
+
for durability on a power loss.
|
|
76
|
+
|
|
77
|
+
### Changed
|
|
78
|
+
|
|
79
|
+
- **docs:** SECURITY.md updated for the permissions policy, the symlink guard
|
|
80
|
+
and the plan-mode MCP caveat; DEVELOPING.md module sizes refreshed.
|
|
81
|
+
- **cleanup:** removed the dead exports `listConfiguredServers`,
|
|
82
|
+
`builtinSkills` and `findFileByName`.
|
|
83
|
+
- **tests:** the MCP result cap, `htmlToText` and the DuckDuckGo parser are
|
|
84
|
+
now covered, and `web.ts`/`spinner.ts` gained coverage floors.
|
|
85
|
+
|
|
86
|
+
### Security
|
|
87
|
+
|
|
88
|
+
- Shared `shellQuote` helper for arguments passed to the shell (single quotes
|
|
89
|
+
with internal quotes doubled).
|
|
90
|
+
|
|
10
91
|
## [2.66.1] - 2026-10-07
|
|
11
92
|
|
|
12
93
|
### Changed
|
|
13
94
|
|
|
14
|
-
- README:
|
|
15
|
-
|
|
16
|
-
readme
|
|
17
|
-
`README.ru.md`
|
|
18
|
-
|
|
19
|
-
`npm run build:readme`;
|
|
95
|
+
- README: the Russian translation is now folded into `<details>` directly in
|
|
96
|
+
`README.md`, so it can be read on the npm page without leaving GitHub. npm
|
|
97
|
+
renders the readme with the same GitHub Flavored Markdown as GitHub, and a
|
|
98
|
+
separate `README.ru.md` is not visible on the npm page (npm has one readme
|
|
99
|
+
per package). Both language versions are assembled from
|
|
100
|
+
`docs/readme.{en,ru}.md` by `npm run build:readme`;
|
|
101
|
+
`test/readme-built.test.ts` guards against drift.
|
|
20
102
|
|
|
21
103
|
## [2.66.0] - 2026-10-07
|
|
22
104
|
|
|
@@ -24,8 +106,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
24
106
|
|
|
25
107
|
- **release:** add scripts/release.mts for one-command release prep (G4)
|
|
26
108
|
|
|
27
|
-
### Fixed
|
|
28
|
-
|
|
29
109
|
- **reload:** include commands, run-task and attach-refs in hot-reload (G2)
|
|
30
110
|
|
|
31
111
|
### Changed
|
|
@@ -34,153 +114,144 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
34
114
|
|
|
35
115
|
## [2.65.1] - 2026-10-07
|
|
36
116
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
и без сброса в пустом чате оставался старый `ctx: 302k · 30%` до первой
|
|
55
|
-
отправки. Тест в `test/newchat-verify.test.ts`.
|
|
117
|
+
- Long preview lines no longer create a horizontal scrollbar in the terminal:
|
|
118
|
+
`toolCall`/`toolResult` (LineEditor and spinner) now clip the preview by the
|
|
119
|
+
VISIBLE terminal width (`truncateToWidth`) instead of by character count. A
|
|
120
|
+
long command/result used to print wider than the screen.
|
|
121
|
+
- Ctrl+Delete / Ctrl+Backspace (and Alt+Delete / Alt+Backspace) now delete a
|
|
122
|
+
word in the input line. These escape sequences used to fall through the
|
|
123
|
+
generic key skip and did nothing. Supported variants: `ESC[3;5~` (xterm
|
|
124
|
+
Ctrl+Delete), `ESC[3;3~` (Alt+Delete), `ESC DEL`/`ESC 8`
|
|
125
|
+
(Alt/Ctrl+Backspace) and the kitty protocol `ESC[127;5u` / `ESC[127;3u`.
|
|
126
|
+
New `_deleteWordRight()` method + `test/editor-word-delete.test.ts`.
|
|
127
|
+
- The `/config` menu now keeps the SELECTED item on screen: the window is
|
|
128
|
+
built "backwards" from the cursor, taking into account the rows the group
|
|
129
|
+
headings occupy (previously an index-based `scrollTop` could push the
|
|
130
|
+
selected row below the window).
|
|
131
|
+
- `/new` resets the context counter: `_lastTokenUsage` accumulates PER CHAT,
|
|
132
|
+
and without a reset an empty chat kept showing the old `ctx: 302k · 30%`
|
|
133
|
+
until the first send. Test in `test/newchat-verify.test.ts`.
|
|
56
134
|
|
|
57
135
|
## [2.65.0] - 2026-10-07
|
|
58
136
|
|
|
59
137
|
### Changed
|
|
60
138
|
|
|
61
|
-
- README
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`logo
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
`
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
уходил верхними строками за экран, из-за чего восстановление курсора
|
|
89
|
-
`ESC[lastRows A` попадало не на ту строку и навигация стрелками «не
|
|
90
|
-
скроллила» список. Заодно исправлена мёртвая регулярка снятия ANSI
|
|
91
|
-
(лишний обратный апостроф) в `src/config-menu.ts`.
|
|
139
|
+
- The README screenshot `docs/demo.png` was regenerated in English (it used to
|
|
140
|
+
be in Russian). The reproducible generator `npm run render-demo`
|
|
141
|
+
(`scripts/render-demo.mjs`) renders the real editor layout and theme palette
|
|
142
|
+
through headless Chromium with an isolated profile.
|
|
143
|
+
- README: the ASCII "how it works" diagram was replaced by a Mermaid diagram
|
|
144
|
+
(the arrows no longer "float") plus a textual description in words.
|
|
145
|
+
- Removed unused files: `logo.jpg` (only `logo-small.jpg` is used in the
|
|
146
|
+
README) and the `docs/demo.tape` draft (the VHS render was never generated).
|
|
147
|
+
`logo.jpg` was also removed from the `files` list in package.json.
|
|
148
|
+
|
|
149
|
+
- Terminal repaint after a window resize: the relative erase `ESC[n A` after a
|
|
150
|
+
size change landed on a stale row (the terminal re-flowed the wraps), so the
|
|
151
|
+
status/input were drawn in the wrong place. After a resize the block is
|
|
152
|
+
re-pinned to the bottom (home + padding), and the scrollback history is kept.
|
|
153
|
+
Test in `test/terminal-drift.test.ts`.
|
|
154
|
+
- A live language change (`/config lang`) did not reach two "created once"
|
|
155
|
+
objects: the thinking-phrase spinner took the default language
|
|
156
|
+
(`randomThinkingPhrase()` instead of `randomThinkingPhrase(this.locale)`),
|
|
157
|
+
and `DeepSeekBrowser` printed its notices (`ds.*`) in the language fixed at
|
|
158
|
+
startup. So with an English UI Russian thinking/notice phrases surfaced.
|
|
159
|
+
Added `DeepSeekBrowser.setLocale()`, called from `setConfigRuntime`.
|
|
160
|
+
- The `/config` menu is now drawn as a WINDOW sized to the terminal height
|
|
161
|
+
(previously the whole list of 49 fields was printed as one block). On a short
|
|
162
|
+
terminal the block scrolled off the top, so restoring the cursor with
|
|
163
|
+
`ESC[lastRows A` landed on the wrong row and arrow navigation "did not
|
|
164
|
+
scroll" the list. Also fixed a dead ANSI-strip regex (a stray backtick) in
|
|
165
|
+
`src/config-menu.ts`.
|
|
92
166
|
|
|
93
167
|
## [2.64.1] - 2026-10-07
|
|
94
168
|
|
|
95
169
|
### Changed
|
|
96
170
|
|
|
97
|
-
- README:
|
|
98
|
-
|
|
99
|
-
|
|
171
|
+
- README: added a terminal session screenshot (`docs/demo.png`) under the
|
|
172
|
+
header so the interface is visible immediately; a reproducible VHS demo
|
|
173
|
+
script (`docs/demo.tape`) for a future GIF.
|
|
100
174
|
|
|
101
175
|
## [2.64.0] - 2026-10-06
|
|
102
176
|
|
|
103
177
|
### Added
|
|
104
178
|
|
|
105
|
-
-
|
|
106
|
-
`npm run changelog:draft [ref]`
|
|
107
|
-
|
|
179
|
+
- A CHANGELOG draft generator from conventional commits (BACKLOG D2):
|
|
180
|
+
`npm run changelog:draft [ref]` prints a ready "Unreleased" block from the
|
|
181
|
+
commits after the last tag (or the given ref). Pure module
|
|
108
182
|
`src/changelog.ts` (`parseConventionalCommit` / `groupCommits` /
|
|
109
|
-
`renderChangelogDraft`,
|
|
110
|
-
|
|
183
|
+
`renderChangelogDraft`, unit-tested). The `CHANGELOG.md` file is NOT
|
|
184
|
+
overwritten automatically — the draft is reviewed by hand.
|
|
111
185
|
|
|
112
186
|
### Changed
|
|
113
187
|
|
|
114
|
-
-
|
|
115
|
-
|
|
116
|
-
(`resolveAttachPath` / `inlineAtRefs`) —
|
|
117
|
-
|
|
188
|
+
- Refactor (BACKLOG C3, step 1): attachment path resolution and `@file`
|
|
189
|
+
reference inlining were moved out of `src/index.ts` into
|
|
190
|
+
`src/attach-refs.ts` (`resolveAttachPath` / `inlineAtRefs`) — now a separate
|
|
191
|
+
testable module.
|
|
118
192
|
|
|
119
193
|
## [2.63.1] - 2026-10-06
|
|
120
194
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
подпись `· 1m 50s` исчезала и появлялась. Теперь обновление собирает тот же
|
|
127
|
-
хвост, что и тик. Регрессионный тест в test/compact-statusline.test.ts.
|
|
195
|
+
- Timer flicker in the pre-send pause status: `sendPause()` (both `LineEditor`
|
|
196
|
+
and the non-TTY `SpinnerUI`) repainted the status WITHOUT the
|
|
197
|
+
"elapsed"/task-badge tail, while the animation tick drew it WITH it — once a
|
|
198
|
+
second the `· 1m 50s` label disappeared and reappeared. The update now builds
|
|
199
|
+
the same tail as the tick. Regression test in test/compact-statusline.test.ts.
|
|
128
200
|
|
|
129
201
|
### Changed
|
|
130
202
|
|
|
131
|
-
- README:
|
|
132
|
-
|
|
133
|
-
- Dependabot:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
203
|
+
- README: centered header, larger logo (640px asset, 430px display), a "How
|
|
204
|
+
it works" diagram, a table of contents and a Links section.
|
|
205
|
+
- Dependabot: major updates are ignored (`semver-major`), monthly schedule,
|
|
206
|
+
auto-merge only for patch/minor and only for bot PRs (author and branch
|
|
207
|
+
checks); auto-merge workflow. The `master` branch is protected by a ruleset:
|
|
208
|
+
PR required, required check is the stable `ci` job.
|
|
137
209
|
|
|
138
210
|
## [2.63.0] - 2026-10-06
|
|
139
211
|
|
|
140
212
|
### Added
|
|
141
213
|
|
|
142
|
-
-
|
|
143
|
-
`.zames/permissions.json` (`default` + `rules`
|
|
144
|
-
`path`
|
|
145
|
-
|
|
146
|
-
`onAskPermission` (
|
|
147
|
-
|
|
148
|
-
(test/permissions.test.ts, test/permissions-loop.test.ts).
|
|
149
|
-
|
|
150
|
-
-
|
|
151
|
-
`argument-hint:`
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
214
|
+
- Real action approvals (BACKLOG C1): an approval policy in
|
|
215
|
+
`.zames/permissions.json` (`default` + `rules` with regexp on
|
|
216
|
+
`tool`/`command`/`path` and an `allow`|`deny`|`ask` action). Evaluated in
|
|
217
|
+
`agent-loop.ts` BEFORE every tool: `deny` blocks the call, `ask` prompts the
|
|
218
|
+
operator via `onAskPermission` (in a TTY — an interactive prompt above the
|
|
219
|
+
input line, in non-TTY — denied). New pure module `src/permissions.ts` +
|
|
220
|
+
tests (test/permissions.test.ts, test/permissions-loop.test.ts).
|
|
221
|
+
|
|
222
|
+
- Custom commands: arguments and hints (BACKLOG B7). Frontmatter
|
|
223
|
+
`argument-hint:` is shown in the "/" list and in `/help` (not inserted into
|
|
224
|
+
the input line), and `arguments:` declares mandatory positional arguments.
|
|
225
|
+
`$1 $2`, named `$name` and the previous `{{args}}`/`$ARGUMENTS` are
|
|
226
|
+
substituted into the command body. If the required arguments are missing the
|
|
227
|
+
command is not sent to the chat and a hint is printed. New pure helpers
|
|
156
228
|
`splitCommandArgs()` / `expandCommandArgs()` / `missingCommandArgs()`
|
|
157
|
-
(src/commands.ts,
|
|
158
|
-
|
|
159
|
-
- Path-scoped
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
`## Scoped instructions (...)` —
|
|
164
|
-
|
|
165
|
-
`renderContextSection()` (src/context.ts, src/system-prompt.ts;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
`src/path-token.ts` (чистый, покрыт тестами).
|
|
229
|
+
(src/commands.ts, unit-tested).
|
|
230
|
+
|
|
231
|
+
- Path-scoped rules (BACKLOG B6): nested `AGENTS.md`/`MEMORY.md` from the
|
|
232
|
+
subdirectories the task touches are pulled in automatically. The task text is
|
|
233
|
+
scanned for path tokens, the nearest instructions are read for the found
|
|
234
|
+
directories (and their ancestors below the working dir) and rendered as a
|
|
235
|
+
separate `## Scoped instructions (...)` section — explicitly marked as
|
|
236
|
+
applying only to those files. `loadProjectContext(workdir, touchPaths?)` and
|
|
237
|
+
`renderContextSection()` (src/context.ts, src/system-prompt.ts; unit-tested).
|
|
238
|
+
|
|
239
|
+
- `@file` references in a task (BACKLOG B5): `solve the task @src/browser.ts`
|
|
240
|
+
inlines the contents of the referenced file right into the task, saving a
|
|
241
|
+
separate agent turn to read it. `@path` is recognized at a word boundary (at
|
|
242
|
+
the start of a line or after a space/bracket/quote) with a file extension;
|
|
243
|
+
`user@host` and decorators (`@Component`) are not touched. Existing files are
|
|
244
|
+
inlined (60 KB per file, 200 KB total — beyond that a truncation marker),
|
|
245
|
+
missing ones stay as-is. Helper `extractAtFileRefs()` in `src/path-token.ts`
|
|
246
|
+
(pure, unit-tested).
|
|
176
247
|
|
|
177
248
|
### Changed
|
|
178
249
|
|
|
179
|
-
- `AGENTS.md`
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
disclosure —
|
|
183
|
-
|
|
250
|
+
- `AGENTS.md` trimmed (BACKLOG C2): the deep root-cause write-ups ("the agent
|
|
251
|
+
stopped", reading the answer from the DOM, send hooks, `LineEditor`,
|
|
252
|
+
attachments) and the terminal mechanics were moved to
|
|
253
|
+
`docs/DESIGN-NOTES.md` (progressive disclosure — not loaded into every task).
|
|
254
|
+
AGENTS.md keeps the acting rules plus a short summary with a pointer.
|
|
184
255
|
|
|
185
256
|
## [2.62.0]
|
|
186
257
|
|
|
@@ -305,8 +376,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
305
376
|
- config, sessions and undo now share the single atomic writer (the
|
|
306
377
|
temp-file+rename logic used to be copy-pasted in three places).
|
|
307
378
|
|
|
308
|
-
### Fixed
|
|
309
|
-
|
|
310
379
|
- BACKLOG.md is now git-ignored and untracked: it is the agent's own
|
|
311
380
|
improvement-notes file, rewritten constantly, and used to be published with
|
|
312
381
|
the package.
|
|
@@ -343,8 +412,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
343
412
|
- `npm run build` now wipes `dist/` first (`scripts/clean-dist.mjs`), so a
|
|
344
413
|
removed source file no longer leaves an orphan `.js` in the package.
|
|
345
414
|
|
|
346
|
-
### Fixed
|
|
347
|
-
|
|
348
415
|
- `/undo` failure reasons are localized: `src/undo.ts` returned hardcoded
|
|
349
416
|
Russian strings that showed up in an English UI. It now returns machine codes
|
|
350
417
|
('empty' / 'disabled') localized by the caller.
|
|
@@ -379,8 +446,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
379
446
|
(diff/apply) stays `.ts`-only.
|
|
380
447
|
- `package.json` gained `packageManager: npm@11.19.0`.
|
|
381
448
|
|
|
382
|
-
### Fixed
|
|
383
|
-
|
|
384
449
|
- The status line no longer prefixes a running-tool indicator with the
|
|
385
450
|
previous answer's "done" phase (e.g. `✓ done: running Bash`): `toolCall()`
|
|
386
451
|
clears the send-phase state before starting the tool animation.
|
|
@@ -416,8 +481,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
416
481
|
|
|
417
482
|
## [2.53.1]
|
|
418
483
|
|
|
419
|
-
### Fixed
|
|
420
|
-
|
|
421
484
|
- `require('fs')` in ESM modules (`src/attachments.ts`, `src/self-review.ts`)
|
|
422
485
|
threw `ReferenceError` at runtime: WSL clipboard detection always returned
|
|
423
486
|
`false` and `/self-review` found no sources when run from the built `dist/`.
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center">
|
|
22
|
-
<strong>English</strong> | <a href="
|
|
22
|
+
<strong>English</strong> | <a href="#readme-ru">Русский</a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
A terminal coding agent that works on top of [chat.deepseek.com](https://chat.deepseek.com/) through Playwright.
|
|
@@ -486,6 +486,7 @@ first-party API client.
|
|
|
486
486
|
|
|
487
487
|
MIT
|
|
488
488
|
|
|
489
|
+
<a id="readme-ru" name="readme-ru"></a>
|
|
489
490
|
<details>
|
|
490
491
|
<summary>🇷🇺 Читать по-русски (Russian)</summary>
|
|
491
492
|
|
package/dist/attachments.js
CHANGED
|
@@ -399,23 +399,6 @@ export function writeClipboardText(text) {
|
|
|
399
399
|
}
|
|
400
400
|
return { ok: false, via: candidates.map((c) => c.bin).join('/') };
|
|
401
401
|
}
|
|
402
|
-
export async function findFileByName(workdir, name) {
|
|
403
|
-
const candidate = path.resolve(workdir, name);
|
|
404
|
-
if (await fs.stat(candidate).catch(() => null))
|
|
405
|
-
return candidate;
|
|
406
|
-
try {
|
|
407
|
-
const { glob } = await import('fs/promises');
|
|
408
|
-
for await (const f of glob('**/' + name, { cwd: workdir })) {
|
|
409
|
-
const abs = path.resolve(workdir, f);
|
|
410
|
-
const rel = path.relative(workdir, abs);
|
|
411
|
-
if (rel.startsWith('..') || path.isAbsolute(rel))
|
|
412
|
-
continue;
|
|
413
|
-
return abs;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
catch { }
|
|
417
|
-
return null;
|
|
418
|
-
}
|
|
419
402
|
// Read file paths from the Windows clipboard (copied files, CF_HDROP) and
|
|
420
403
|
// translate them to WSL paths. On WSL the user often copies a FILE, not an
|
|
421
404
|
// image. Returns [] on non-WSL / when nothing is there.
|
package/dist/context.js
CHANGED
package/dist/extraTools.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { safePath } from './sandbox.js';
|
|
3
4
|
// Extra tools that bring zames closer to Claude Code / Codex CLI:
|
|
4
5
|
// * LS - list a directory (Claude Code has LS; Codex has list_dir)
|
|
5
6
|
// * MultiEdit - several edits to ONE file applied atomically (Claude Code)
|
|
@@ -171,14 +172,7 @@ export function applyUpdateHunk(content, hunkLines) {
|
|
|
171
172
|
export function createExtraTools(workdir, { undo, todos } = {}) {
|
|
172
173
|
const todoStore = todos || defaultTodoStore;
|
|
173
174
|
const root = path.resolve(workdir);
|
|
174
|
-
const safe = (p) =>
|
|
175
|
-
const resolved = path.resolve(root, p);
|
|
176
|
-
const rel = path.relative(root, resolved);
|
|
177
|
-
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
178
|
-
throw new Error('Access outside the working directory is forbidden: ' + p);
|
|
179
|
-
}
|
|
180
|
-
return resolved;
|
|
181
|
-
};
|
|
175
|
+
const safe = (p) => safePath(root, p);
|
|
182
176
|
// Guard against missing/placeholder required args: a missing `path` becomes
|
|
183
177
|
// String(undefined) === "undefined" and would create a file literally named
|
|
184
178
|
// "undefined" in the working directory.
|
|
@@ -271,9 +265,14 @@ export function createExtraTools(workdir, { undo, todos } = {}) {
|
|
|
271
265
|
throw new Error(`edits[${i}]: string occurs ${occurrences} times. ` +
|
|
272
266
|
'Make old_string more specific or pass replace_all=true.');
|
|
273
267
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
268
|
+
if (e.replace_all) {
|
|
269
|
+
content = content.split(oldStr).join(newStr);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// Functional replacer: a string replacer would interpret $-patterns
|
|
273
|
+
// ($&, $$, $1) inside newStr and corrupt the written text.
|
|
274
|
+
content = content.replace(oldStr, () => newStr);
|
|
275
|
+
}
|
|
277
276
|
}
|
|
278
277
|
if (undo)
|
|
279
278
|
await undo.backup(file);
|
package/dist/fsutil.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
// One atomic writer shared by config, sessions and undo. Writing to a temp
|
|
3
4
|
// file in the SAME directory and renaming over the target is atomic on POSIX
|
|
4
5
|
// and Windows (same volume), so a crash or Ctrl+C mid-write can never leave a
|
|
@@ -8,10 +9,46 @@ import fs from 'fs';
|
|
|
8
9
|
// Kept in its own module because the pattern used to be copy-pasted in at
|
|
9
10
|
// least three places; drift between the copies is exactly how one of them
|
|
10
11
|
// silently loses its atomicity.
|
|
12
|
+
//
|
|
13
|
+
// The file is created 0o600: ~/.zames/config.json holds the
|
|
14
|
+
// browser password in clear text alongside sessions/undo history, and the
|
|
15
|
+
// default umask (022) would make them readable by other users on a
|
|
16
|
+
// multi-user machine. We also chmod the target after rename because a
|
|
17
|
+
// pre-existing file keeps its old mode (rename does not change the inode).
|
|
11
18
|
export function writeFileAtomic(file, data) {
|
|
12
19
|
const tmp = file + '.tmp-' + process.pid;
|
|
13
|
-
|
|
20
|
+
// fsync the file BEFORE rename: without it a power loss can leave the
|
|
21
|
+
// renamed name pointing at an empty/partial inode (rename itself is atomic,
|
|
22
|
+
// but the data blocks may not have reached the disk yet).
|
|
23
|
+
const fd = fs.openSync(tmp, 'w', 0o600);
|
|
24
|
+
try {
|
|
25
|
+
fs.writeFileSync(fd, data);
|
|
26
|
+
fs.fsyncSync(fd);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
fs.closeSync(fd);
|
|
30
|
+
}
|
|
14
31
|
fs.renameSync(tmp, file);
|
|
32
|
+
// fsync the DIRECTORY so the rename entry itself is durable.
|
|
33
|
+
try {
|
|
34
|
+
const dirFd = fs.openSync(path.dirname(file), 'r');
|
|
35
|
+
try {
|
|
36
|
+
fs.fsyncSync(dirFd);
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
fs.closeSync(dirFd);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Some platforms/filesystems do not allow opening a directory; best-effort.
|
|
44
|
+
}
|
|
45
|
+
// Best-effort: on Windows chmod is largely a no-op and can throw.
|
|
46
|
+
try {
|
|
47
|
+
fs.chmodSync(file, 0o600);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// ignore
|
|
51
|
+
}
|
|
15
52
|
}
|
|
16
53
|
export function writeJsonAtomic(file, value) {
|
|
17
54
|
writeFileAtomic(file, JSON.stringify(value, null, 2) + String.fromCharCode(10));
|