dscli-agent 0.5.0__tar.gz → 0.6.0__tar.gz
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.
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/PKG-INFO +52 -21
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/README.md +51 -20
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/__init__.py +1 -1
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/agent.py +109 -35
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/cli.py +69 -6
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/compaction.py +6 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/config.py +1 -2
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/display.py +10 -3
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/prompts.py +30 -12
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/protocol.py +6 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/tools.py +111 -18
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/tui.py +323 -71
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/ui.py +0 -17
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli_agent.egg-info/PKG-INFO +52 -21
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/pyproject.toml +3 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_agent.py +103 -16
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_cli.py +88 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_compaction.py +26 -1
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_display.py +2 -2
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_protocol.py +8 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_tools.py +64 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_tui.py +150 -14
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/LICENSE +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/__main__.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/attachments.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/branding.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/browser.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/providers.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/scraper.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli/tokens.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli_agent.egg-info/SOURCES.txt +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli_agent.egg-info/dependency_links.txt +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli_agent.egg-info/entry_points.txt +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli_agent.egg-info/requires.txt +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/dscli_agent.egg-info/top_level.txt +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/setup.cfg +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_branding.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_browser.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_config.py +0 -0
- {dscli_agent-0.5.0 → dscli_agent-0.6.0}/tests/test_tokens.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dscli-agent
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: A local coding agent that uses DeepSeek web chat as its model
|
|
5
5
|
Author: Anish
|
|
6
6
|
License-Expression: MIT
|
|
@@ -106,11 +106,33 @@ dscli
|
|
|
106
106
|
|
|
107
107
|
dscli shows an inline terminal UI inspired by Claude Code: a cleared screen and gradient
|
|
108
108
|
ASCII wordmark on launch, an animated spinner with rotating status verbs, a smoothly
|
|
109
|
-
revealed streaming answer,
|
|
110
|
-
|
|
109
|
+
revealed streaming answer, tree-style tool cards, approval prompts that name the action
|
|
110
|
+
and path without printing file contents, and the final answer rendered as Markdown.
|
|
111
|
+
|
|
112
|
+
Each tool call renders as a card with a short result summary and, when there is more to
|
|
113
|
+
see, a preview of the output:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
▪ Read(dscli/config.py · lines 1-40)
|
|
117
|
+
└ Read lines 1-40 of 120 (Ctrl+O to expand)
|
|
118
|
+
|
|
119
|
+
▪ Edit(dscli/config.py)
|
|
120
|
+
└ Accepted config.py (Ctrl+O to expand)
|
|
121
|
+
|
|
122
|
+
▪ Bash(grep -n "read_file" dscli/prompts.py)
|
|
123
|
+
└ 121:- Do NOT use read_file, write_file, apply_patch, list_files...
|
|
124
|
+
243: non-empty plan. Start by investigating with read_file and list_files
|
|
125
|
+
… +2 lines (Ctrl+O to expand)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Failed calls keep the `▪` header and show the error under the `└` branch. Long paths are
|
|
129
|
+
shortened in the middle and long commands are trimmed to keep the card on one line.
|
|
130
|
+
Press `Ctrl+O` at the prompt to print the full output of the most recent tool card or
|
|
131
|
+
thinking block.
|
|
111
132
|
Accents use a muted palette that matches the wordmark: sage for success, dusty rose for
|
|
112
|
-
errors, and soft teal for
|
|
113
|
-
|
|
133
|
+
errors, and soft teal for highlights. Markdown answers — streamed, interrupted, and
|
|
134
|
+
final — use the same palette for headings, links, inline code, and fenced code
|
|
135
|
+
highlighting. The same muted colors apply to the plain and non-TTY output.
|
|
114
136
|
Output stays in normal scrollback after startup; dscli does not take over the full
|
|
115
137
|
screen, and the welcome card shows the workspace, model, active modes, and shortcuts.
|
|
116
138
|
Plans
|
|
@@ -132,12 +154,12 @@ DeepThink is always enabled, and the UI streams the model's reasoning live in a
|
|
|
132
154
|
compact block above the reply:
|
|
133
155
|
|
|
134
156
|
```text
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
157
|
+
Thinking
|
|
158
|
+
│ I should inspect the configuration first.
|
|
159
|
+
│ … +12 rows (Ctrl+O to expand)
|
|
160
|
+
│ charlie line
|
|
161
|
+
│ delta line
|
|
162
|
+
│ echo line
|
|
141
163
|
```
|
|
142
164
|
|
|
143
165
|
The first sentence stays pinned at the top while the newest three wrapped rows
|
|
@@ -182,6 +204,11 @@ runs before every model turn, so it also works mid-task; the TUI shows a
|
|
|
182
204
|
`⟳ Compacted context at 1.0M tokens → … · new chat` notice. `/new` clears the recorded
|
|
183
205
|
conversation as well.
|
|
184
206
|
|
|
207
|
+
Type `/compact` in `dscli chat` to compact on demand. dscli asks the current chat for the
|
|
208
|
+
summary, writes the compaction file, and starts a new DeepSeek chat; the next message you
|
|
209
|
+
send resumes the compacted conversation there, with the compaction attached. `/compact`
|
|
210
|
+
needs at least one earlier message, and `/new` discards a pending compaction.
|
|
211
|
+
|
|
185
212
|
### Deeplanning mode
|
|
186
213
|
|
|
187
214
|
```powershell
|
|
@@ -208,14 +235,15 @@ every run with `dscli config --set deeplanning=true`, or turn it off for one run
|
|
|
208
235
|
| `Ctrl+C` | While the agent is working: interrupt the current turn, keep the browser session, and return to the prompt. At the prompt: clear the input. |
|
|
209
236
|
| `Ctrl+D` | Exit dscli. |
|
|
210
237
|
| `↑` / `↓` | Browse previous messages. |
|
|
211
|
-
| `Ctrl+O` | Expand the last thinking block in full. |
|
|
238
|
+
| `Ctrl+O` | Expand the last thinking block or tool output in full. |
|
|
212
239
|
|
|
213
240
|
Typing `/` opens an autocomplete menu. History is saved to
|
|
214
241
|
`%APPDATA%\dscli\chat_history` (`~/Library/Application Support/dscli/chat_history` on
|
|
215
242
|
macOS, `~/.config/dscli/chat_history` elsewhere).
|
|
216
243
|
|
|
217
|
-
Slash commands: `/help` lists commands and shortcuts, `/
|
|
218
|
-
conversation
|
|
244
|
+
Slash commands: `/help` lists commands and shortcuts, `/compact` summarizes the
|
|
245
|
+
conversation and continues it in a new chat, `/new` starts a fresh conversation so the
|
|
246
|
+
agent forgets earlier turns, and `/exit` (or `/quit`) closes dscli.
|
|
219
247
|
|
|
220
248
|
If Cloudflare interrupts a headless run, dscli opens the persistent profile in a visible
|
|
221
249
|
Chrome window and resubmits the interrupted prompt. Complete the check in that window.
|
|
@@ -225,8 +253,8 @@ current model turn restarts with the same prompt in a fresh chat, so an answer t
|
|
|
225
253
|
already streaming when the check appeared is fetched again. Set
|
|
226
254
|
`dscli config --set auto_human_check=false` to fail fast instead.
|
|
227
255
|
|
|
228
|
-
When dscli asks to write a file or run a command, it shows the title and
|
|
229
|
-
|
|
256
|
+
When dscli asks to write a file or run a command, it shows the title and, for commands,
|
|
257
|
+
the exact command. File contents and diffs are never printed.
|
|
230
258
|
Answer `y` to approve once, `n` to deny, `a` to approve everything for the rest of the
|
|
231
259
|
session, or `q` to quit.
|
|
232
260
|
|
|
@@ -269,7 +297,7 @@ dscli run --prompt-file task.md -y
|
|
|
269
297
|
dscli run "Review the docs" --no-shell
|
|
270
298
|
```
|
|
271
299
|
|
|
272
|
-
The final answer is printed to stdout. Progress
|
|
300
|
+
The final answer is printed to stdout. Progress and approval prompts go to
|
|
273
301
|
stderr, so `dscli run ... > result.txt` captures only the answer. `run` never uses the
|
|
274
302
|
interactive UI; its output contract is unchanged.
|
|
275
303
|
|
|
@@ -289,7 +317,7 @@ failures and unconfirmed completions are always logged to stderr, including unde
|
|
|
289
317
|
| `--headed` / `--headless` | Show or hide the browser. Headless is the default. |
|
|
290
318
|
| `--search` | Turn DeepSeek's Search mode on for the run (DeepThink is always on). |
|
|
291
319
|
| `--deeplanning` | Require a non-empty `update_plan` before writes or commands (alias: `--deep-planning`). |
|
|
292
|
-
| `--max-steps N` | Maximum model turns (default
|
|
320
|
+
| `--max-steps N` | Maximum model turns (default 20, cap 150). |
|
|
293
321
|
| `--timeout N` | Response timeout per turn in seconds (default 300). |
|
|
294
322
|
| `--verify CMD` | Run CMD automatically after file edits; failures block success. |
|
|
295
323
|
| `--no-verify` | Disable the configured `verify_command` for this run. |
|
|
@@ -357,6 +385,7 @@ creep and unnecessary abstractions, confirm hard-to-reverse actions, reference c
|
|
|
357
385
|
| `write_file` | **Create** a new file. Refuses to overwrite an existing non-empty file. |
|
|
358
386
|
| `apply_patch` | The only way to edit an existing file: exact `search` → `replace`. |
|
|
359
387
|
| `list_files` | Bounded recursive listing. |
|
|
388
|
+
| `search_files` | Search text files for a literal string; returns `path:line` matches. |
|
|
360
389
|
| `delete_file` | Delete one file when explicitly requested (approval required). |
|
|
361
390
|
| `run_command` | Run a command with an approval prompt via PowerShell, cmd.exe, sh, or bash. |
|
|
362
391
|
| `update_plan` | Create or revise the task plan; the task cannot finish while steps are unfinished. |
|
|
@@ -404,8 +433,9 @@ model can modify the files it checks, so review the final diff.
|
|
|
404
433
|
|
|
405
434
|
- Tools are confined to the workspace unless `--allow-outside` is passed.
|
|
406
435
|
- `.git` is never writable through the tools.
|
|
407
|
-
- Every write, patch, delete, and command shows
|
|
408
|
-
`[y]es / [n]o / [a]ll / [q]uit` before changing anything.
|
|
436
|
+
- Every write, patch, delete, and command shows the action and affected path (or the
|
|
437
|
+
exact command) and asks `[y]es / [n]o / [a]ll / [q]uit` before changing anything.
|
|
438
|
+
File contents and diffs are never printed. `-y` skips the prompts.
|
|
409
439
|
- Command execution is enabled by default, but every model-requested command still
|
|
410
440
|
requires approval. The prompt names the interpreter (PowerShell, cmd.exe, sh, or
|
|
411
441
|
bash). `--no-shell` disables model-requested commands for a run.
|
|
@@ -420,7 +450,8 @@ model can modify the files it checks, so review the final diff.
|
|
|
420
450
|
through the browser.
|
|
421
451
|
|
|
422
452
|
This is still an assistant with write access to your files. Use a clean workspace or
|
|
423
|
-
version control, and review
|
|
453
|
+
version control, and review the changes in your VCS after a run; approval prompts show
|
|
454
|
+
only the action and path, not the file contents.
|
|
424
455
|
|
|
425
456
|
## Configuration
|
|
426
457
|
|
|
@@ -76,11 +76,33 @@ dscli
|
|
|
76
76
|
|
|
77
77
|
dscli shows an inline terminal UI inspired by Claude Code: a cleared screen and gradient
|
|
78
78
|
ASCII wordmark on launch, an animated spinner with rotating status verbs, a smoothly
|
|
79
|
-
revealed streaming answer,
|
|
80
|
-
|
|
79
|
+
revealed streaming answer, tree-style tool cards, approval prompts that name the action
|
|
80
|
+
and path without printing file contents, and the final answer rendered as Markdown.
|
|
81
|
+
|
|
82
|
+
Each tool call renders as a card with a short result summary and, when there is more to
|
|
83
|
+
see, a preview of the output:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
▪ Read(dscli/config.py · lines 1-40)
|
|
87
|
+
└ Read lines 1-40 of 120 (Ctrl+O to expand)
|
|
88
|
+
|
|
89
|
+
▪ Edit(dscli/config.py)
|
|
90
|
+
└ Accepted config.py (Ctrl+O to expand)
|
|
91
|
+
|
|
92
|
+
▪ Bash(grep -n "read_file" dscli/prompts.py)
|
|
93
|
+
└ 121:- Do NOT use read_file, write_file, apply_patch, list_files...
|
|
94
|
+
243: non-empty plan. Start by investigating with read_file and list_files
|
|
95
|
+
… +2 lines (Ctrl+O to expand)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Failed calls keep the `▪` header and show the error under the `└` branch. Long paths are
|
|
99
|
+
shortened in the middle and long commands are trimmed to keep the card on one line.
|
|
100
|
+
Press `Ctrl+O` at the prompt to print the full output of the most recent tool card or
|
|
101
|
+
thinking block.
|
|
81
102
|
Accents use a muted palette that matches the wordmark: sage for success, dusty rose for
|
|
82
|
-
errors, and soft teal for
|
|
83
|
-
|
|
103
|
+
errors, and soft teal for highlights. Markdown answers — streamed, interrupted, and
|
|
104
|
+
final — use the same palette for headings, links, inline code, and fenced code
|
|
105
|
+
highlighting. The same muted colors apply to the plain and non-TTY output.
|
|
84
106
|
Output stays in normal scrollback after startup; dscli does not take over the full
|
|
85
107
|
screen, and the welcome card shows the workspace, model, active modes, and shortcuts.
|
|
86
108
|
Plans
|
|
@@ -102,12 +124,12 @@ DeepThink is always enabled, and the UI streams the model's reasoning live in a
|
|
|
102
124
|
compact block above the reply:
|
|
103
125
|
|
|
104
126
|
```text
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
127
|
+
Thinking
|
|
128
|
+
│ I should inspect the configuration first.
|
|
129
|
+
│ … +12 rows (Ctrl+O to expand)
|
|
130
|
+
│ charlie line
|
|
131
|
+
│ delta line
|
|
132
|
+
│ echo line
|
|
111
133
|
```
|
|
112
134
|
|
|
113
135
|
The first sentence stays pinned at the top while the newest three wrapped rows
|
|
@@ -152,6 +174,11 @@ runs before every model turn, so it also works mid-task; the TUI shows a
|
|
|
152
174
|
`⟳ Compacted context at 1.0M tokens → … · new chat` notice. `/new` clears the recorded
|
|
153
175
|
conversation as well.
|
|
154
176
|
|
|
177
|
+
Type `/compact` in `dscli chat` to compact on demand. dscli asks the current chat for the
|
|
178
|
+
summary, writes the compaction file, and starts a new DeepSeek chat; the next message you
|
|
179
|
+
send resumes the compacted conversation there, with the compaction attached. `/compact`
|
|
180
|
+
needs at least one earlier message, and `/new` discards a pending compaction.
|
|
181
|
+
|
|
155
182
|
### Deeplanning mode
|
|
156
183
|
|
|
157
184
|
```powershell
|
|
@@ -178,14 +205,15 @@ every run with `dscli config --set deeplanning=true`, or turn it off for one run
|
|
|
178
205
|
| `Ctrl+C` | While the agent is working: interrupt the current turn, keep the browser session, and return to the prompt. At the prompt: clear the input. |
|
|
179
206
|
| `Ctrl+D` | Exit dscli. |
|
|
180
207
|
| `↑` / `↓` | Browse previous messages. |
|
|
181
|
-
| `Ctrl+O` | Expand the last thinking block in full. |
|
|
208
|
+
| `Ctrl+O` | Expand the last thinking block or tool output in full. |
|
|
182
209
|
|
|
183
210
|
Typing `/` opens an autocomplete menu. History is saved to
|
|
184
211
|
`%APPDATA%\dscli\chat_history` (`~/Library/Application Support/dscli/chat_history` on
|
|
185
212
|
macOS, `~/.config/dscli/chat_history` elsewhere).
|
|
186
213
|
|
|
187
|
-
Slash commands: `/help` lists commands and shortcuts, `/
|
|
188
|
-
conversation
|
|
214
|
+
Slash commands: `/help` lists commands and shortcuts, `/compact` summarizes the
|
|
215
|
+
conversation and continues it in a new chat, `/new` starts a fresh conversation so the
|
|
216
|
+
agent forgets earlier turns, and `/exit` (or `/quit`) closes dscli.
|
|
189
217
|
|
|
190
218
|
If Cloudflare interrupts a headless run, dscli opens the persistent profile in a visible
|
|
191
219
|
Chrome window and resubmits the interrupted prompt. Complete the check in that window.
|
|
@@ -195,8 +223,8 @@ current model turn restarts with the same prompt in a fresh chat, so an answer t
|
|
|
195
223
|
already streaming when the check appeared is fetched again. Set
|
|
196
224
|
`dscli config --set auto_human_check=false` to fail fast instead.
|
|
197
225
|
|
|
198
|
-
When dscli asks to write a file or run a command, it shows the title and
|
|
199
|
-
|
|
226
|
+
When dscli asks to write a file or run a command, it shows the title and, for commands,
|
|
227
|
+
the exact command. File contents and diffs are never printed.
|
|
200
228
|
Answer `y` to approve once, `n` to deny, `a` to approve everything for the rest of the
|
|
201
229
|
session, or `q` to quit.
|
|
202
230
|
|
|
@@ -239,7 +267,7 @@ dscli run --prompt-file task.md -y
|
|
|
239
267
|
dscli run "Review the docs" --no-shell
|
|
240
268
|
```
|
|
241
269
|
|
|
242
|
-
The final answer is printed to stdout. Progress
|
|
270
|
+
The final answer is printed to stdout. Progress and approval prompts go to
|
|
243
271
|
stderr, so `dscli run ... > result.txt` captures only the answer. `run` never uses the
|
|
244
272
|
interactive UI; its output contract is unchanged.
|
|
245
273
|
|
|
@@ -259,7 +287,7 @@ failures and unconfirmed completions are always logged to stderr, including unde
|
|
|
259
287
|
| `--headed` / `--headless` | Show or hide the browser. Headless is the default. |
|
|
260
288
|
| `--search` | Turn DeepSeek's Search mode on for the run (DeepThink is always on). |
|
|
261
289
|
| `--deeplanning` | Require a non-empty `update_plan` before writes or commands (alias: `--deep-planning`). |
|
|
262
|
-
| `--max-steps N` | Maximum model turns (default
|
|
290
|
+
| `--max-steps N` | Maximum model turns (default 20, cap 150). |
|
|
263
291
|
| `--timeout N` | Response timeout per turn in seconds (default 300). |
|
|
264
292
|
| `--verify CMD` | Run CMD automatically after file edits; failures block success. |
|
|
265
293
|
| `--no-verify` | Disable the configured `verify_command` for this run. |
|
|
@@ -327,6 +355,7 @@ creep and unnecessary abstractions, confirm hard-to-reverse actions, reference c
|
|
|
327
355
|
| `write_file` | **Create** a new file. Refuses to overwrite an existing non-empty file. |
|
|
328
356
|
| `apply_patch` | The only way to edit an existing file: exact `search` → `replace`. |
|
|
329
357
|
| `list_files` | Bounded recursive listing. |
|
|
358
|
+
| `search_files` | Search text files for a literal string; returns `path:line` matches. |
|
|
330
359
|
| `delete_file` | Delete one file when explicitly requested (approval required). |
|
|
331
360
|
| `run_command` | Run a command with an approval prompt via PowerShell, cmd.exe, sh, or bash. |
|
|
332
361
|
| `update_plan` | Create or revise the task plan; the task cannot finish while steps are unfinished. |
|
|
@@ -374,8 +403,9 @@ model can modify the files it checks, so review the final diff.
|
|
|
374
403
|
|
|
375
404
|
- Tools are confined to the workspace unless `--allow-outside` is passed.
|
|
376
405
|
- `.git` is never writable through the tools.
|
|
377
|
-
- Every write, patch, delete, and command shows
|
|
378
|
-
`[y]es / [n]o / [a]ll / [q]uit` before changing anything.
|
|
406
|
+
- Every write, patch, delete, and command shows the action and affected path (or the
|
|
407
|
+
exact command) and asks `[y]es / [n]o / [a]ll / [q]uit` before changing anything.
|
|
408
|
+
File contents and diffs are never printed. `-y` skips the prompts.
|
|
379
409
|
- Command execution is enabled by default, but every model-requested command still
|
|
380
410
|
requires approval. The prompt names the interpreter (PowerShell, cmd.exe, sh, or
|
|
381
411
|
bash). `--no-shell` disables model-requested commands for a run.
|
|
@@ -390,7 +420,8 @@ model can modify the files it checks, so review the final diff.
|
|
|
390
420
|
through the browser.
|
|
391
421
|
|
|
392
422
|
This is still an assistant with write access to your files. Use a clean workspace or
|
|
393
|
-
version control, and review
|
|
423
|
+
version control, and review the changes in your VCS after a run; approval prompts show
|
|
424
|
+
only the action and path, not the file contents.
|
|
394
425
|
|
|
395
426
|
## Configuration
|
|
396
427
|
|
|
@@ -10,7 +10,12 @@ from typing import Callable
|
|
|
10
10
|
|
|
11
11
|
from .attachments import Attachment
|
|
12
12
|
from .browser import ChatSession
|
|
13
|
-
from .compaction import
|
|
13
|
+
from .compaction import (
|
|
14
|
+
Compaction,
|
|
15
|
+
ConversationLog,
|
|
16
|
+
extract_compact_summary,
|
|
17
|
+
write_compaction,
|
|
18
|
+
)
|
|
14
19
|
from .config import Config
|
|
15
20
|
from .display import AgentDisplay, PlainDisplay
|
|
16
21
|
from .prompts import (
|
|
@@ -59,6 +64,73 @@ class AgentOutcome:
|
|
|
59
64
|
completed: bool = False
|
|
60
65
|
|
|
61
66
|
|
|
67
|
+
def request_compaction_summary(
|
|
68
|
+
*,
|
|
69
|
+
session: ChatSession,
|
|
70
|
+
display: AgentDisplay,
|
|
71
|
+
task: str,
|
|
72
|
+
plan: list[PlanStep] | None,
|
|
73
|
+
changed: list[str],
|
|
74
|
+
manual: bool = False,
|
|
75
|
+
) -> str:
|
|
76
|
+
display.compaction_summarizing()
|
|
77
|
+
prompt = build_compact_prompt(
|
|
78
|
+
task=task,
|
|
79
|
+
plan=plan,
|
|
80
|
+
changed=changed,
|
|
81
|
+
manual=manual,
|
|
82
|
+
)
|
|
83
|
+
try:
|
|
84
|
+
reply = session.send_and_wait(prompt)
|
|
85
|
+
except Exception as exc:
|
|
86
|
+
log.warning("Could not request a compaction summary: %s", exc)
|
|
87
|
+
return ""
|
|
88
|
+
summary = extract_compact_summary(reply)
|
|
89
|
+
if not summary:
|
|
90
|
+
log.warning(
|
|
91
|
+
"The model's compaction summary was empty or unreadable; "
|
|
92
|
+
"using the activity log only."
|
|
93
|
+
)
|
|
94
|
+
return summary
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def compact_conversation(
|
|
98
|
+
*,
|
|
99
|
+
session: ChatSession,
|
|
100
|
+
toolbox: Toolbox,
|
|
101
|
+
workspace: Path,
|
|
102
|
+
task: str,
|
|
103
|
+
history: ConversationLog,
|
|
104
|
+
display: AgentDisplay,
|
|
105
|
+
verification: str = "",
|
|
106
|
+
) -> Compaction:
|
|
107
|
+
"""Summarize the chat, write the compaction file, and open a new chat."""
|
|
108
|
+
summary = request_compaction_summary(
|
|
109
|
+
session=session,
|
|
110
|
+
display=display,
|
|
111
|
+
task=task,
|
|
112
|
+
plan=toolbox.plan,
|
|
113
|
+
changed=toolbox.changed_files,
|
|
114
|
+
manual=True,
|
|
115
|
+
)
|
|
116
|
+
used = int(getattr(session, "context_tokens", 0) or 0)
|
|
117
|
+
compaction = write_compaction(
|
|
118
|
+
workspace=workspace,
|
|
119
|
+
task=task,
|
|
120
|
+
log=history,
|
|
121
|
+
plan=toolbox.plan,
|
|
122
|
+
changed=toolbox.changed_files,
|
|
123
|
+
verification=verification,
|
|
124
|
+
context_tokens=used,
|
|
125
|
+
project_tree=project_structure(workspace),
|
|
126
|
+
summary=summary,
|
|
127
|
+
)
|
|
128
|
+
session.new_chat()
|
|
129
|
+
session.set_modes()
|
|
130
|
+
display.compaction_started(str(compaction.path), used)
|
|
131
|
+
return compaction
|
|
132
|
+
|
|
133
|
+
|
|
62
134
|
class Agent:
|
|
63
135
|
def __init__(
|
|
64
136
|
self,
|
|
@@ -76,6 +148,7 @@ class Agent:
|
|
|
76
148
|
on_thinking_snapshot: Callable[[str], None] | None = None,
|
|
77
149
|
display: AgentDisplay | None = None,
|
|
78
150
|
history: ConversationLog | None = None,
|
|
151
|
+
resume: Compaction | None = None,
|
|
79
152
|
) -> None:
|
|
80
153
|
self.session = session
|
|
81
154
|
self.cfg = config
|
|
@@ -85,6 +158,7 @@ class Agent:
|
|
|
85
158
|
self.attachments = attachments
|
|
86
159
|
self.verbose = verbose
|
|
87
160
|
self.followup = followup
|
|
161
|
+
self.resume = resume
|
|
88
162
|
self.on_final_snapshot = on_final_snapshot
|
|
89
163
|
self.on_thinking_snapshot = on_thinking_snapshot
|
|
90
164
|
self.display = display or PlainDisplay(
|
|
@@ -120,24 +194,13 @@ class Agent:
|
|
|
120
194
|
return self._verify_command or "(none configured)"
|
|
121
195
|
|
|
122
196
|
def _request_compaction_summary(self, plan: list[PlanStep] | None) -> str:
|
|
123
|
-
|
|
124
|
-
|
|
197
|
+
return request_compaction_summary(
|
|
198
|
+
session=self.session,
|
|
199
|
+
display=self.display,
|
|
125
200
|
task=self.task,
|
|
126
201
|
plan=plan,
|
|
127
202
|
changed=self.toolbox.changed_files,
|
|
128
203
|
)
|
|
129
|
-
try:
|
|
130
|
-
reply = self.session.send_and_wait(prompt)
|
|
131
|
-
except Exception as exc:
|
|
132
|
-
log.warning("Could not request a compaction summary: %s", exc)
|
|
133
|
-
return ""
|
|
134
|
-
summary = extract_compact_summary(reply)
|
|
135
|
-
if not summary:
|
|
136
|
-
log.warning(
|
|
137
|
-
"The model's compaction summary was empty or unreadable; "
|
|
138
|
-
"using the activity log only."
|
|
139
|
-
)
|
|
140
|
-
return summary
|
|
141
204
|
|
|
142
205
|
def _prepare_send(
|
|
143
206
|
self,
|
|
@@ -285,7 +348,30 @@ class Agent:
|
|
|
285
348
|
self._sent_any = False
|
|
286
349
|
self.history.add_task(self.task)
|
|
287
350
|
|
|
288
|
-
if self.
|
|
351
|
+
if self.resume is not None:
|
|
352
|
+
self.history.add_note(
|
|
353
|
+
f"Resumed from {self.resume.path.name} in a new chat."
|
|
354
|
+
)
|
|
355
|
+
initial = build_resume_prompt(
|
|
356
|
+
task=self.task,
|
|
357
|
+
workspace=self.workspace,
|
|
358
|
+
compaction=self.resume.text,
|
|
359
|
+
workspace_tree=workspace_preview(self.workspace),
|
|
360
|
+
plan=self.toolbox.plan,
|
|
361
|
+
live_plan=self.toolbox.plan,
|
|
362
|
+
changed=self.toolbox.changed_files,
|
|
363
|
+
attachments=self.attachments,
|
|
364
|
+
allow_shell=self.cfg.shell_enabled,
|
|
365
|
+
allow_outside=self.cfg.allow_outside,
|
|
366
|
+
max_steps=self.cfg.max_steps,
|
|
367
|
+
verify_command=self._verify_command,
|
|
368
|
+
compaction_name=self.resume.path.name,
|
|
369
|
+
deeplanning=self.cfg.deeplanning,
|
|
370
|
+
max_tool_calls=self.cfg.max_tool_calls,
|
|
371
|
+
turn_id=self.turn_id,
|
|
372
|
+
manual=True,
|
|
373
|
+
)
|
|
374
|
+
elif self.followup:
|
|
289
375
|
initial = build_followup_prompt(
|
|
290
376
|
self.task,
|
|
291
377
|
turn_id=self.turn_id,
|
|
@@ -314,10 +400,11 @@ class Agent:
|
|
|
314
400
|
)
|
|
315
401
|
|
|
316
402
|
attachment_paths = [a.path for a in self.attachments]
|
|
403
|
+
if self.resume is not None:
|
|
404
|
+
attachment_paths.insert(0, self.resume.path)
|
|
317
405
|
message = initial
|
|
318
406
|
files = attachment_paths if attachment_paths else None
|
|
319
407
|
turn = 0
|
|
320
|
-
protocol_failures = 0
|
|
321
408
|
last_reply = ""
|
|
322
409
|
|
|
323
410
|
while True:
|
|
@@ -342,13 +429,7 @@ class Agent:
|
|
|
342
429
|
try:
|
|
343
430
|
step = parse_step(last_reply)
|
|
344
431
|
except ProtocolError as exc:
|
|
345
|
-
protocol_failures += 1
|
|
346
432
|
self.session.save_last_response(last_reply, "protocol-error")
|
|
347
|
-
if protocol_failures > self.cfg.max_protocol_retries:
|
|
348
|
-
raise ProtocolError(
|
|
349
|
-
f"{exc} The raw reply was saved to the dscli debug directory.",
|
|
350
|
-
last_reply,
|
|
351
|
-
) from exc
|
|
352
433
|
self.display.protocol_error(str(exc))
|
|
353
434
|
if turn >= self.cfg.max_steps:
|
|
354
435
|
return self._wrap_up(turn, last_reply or str(exc))
|
|
@@ -363,13 +444,7 @@ class Agent:
|
|
|
363
444
|
|
|
364
445
|
turn_error = self._turn_id_error(step)
|
|
365
446
|
if turn_error is not None:
|
|
366
|
-
protocol_failures += 1
|
|
367
447
|
self.session.save_last_response(last_reply, "turn-id-error")
|
|
368
|
-
if protocol_failures > self.cfg.max_protocol_retries:
|
|
369
|
-
raise ProtocolError(
|
|
370
|
-
f"{turn_error} The raw reply was saved to the dscli debug directory.",
|
|
371
|
-
last_reply,
|
|
372
|
-
)
|
|
373
448
|
self.display.protocol_error(turn_error)
|
|
374
449
|
if turn >= self.cfg.max_steps:
|
|
375
450
|
return self._wrap_up(turn, last_reply)
|
|
@@ -413,14 +488,11 @@ class Agent:
|
|
|
413
488
|
|
|
414
489
|
if not step.tool_calls:
|
|
415
490
|
# Should have been caught by the parser; treat it as a protocol miss.
|
|
416
|
-
protocol_failures += 1
|
|
417
|
-
if protocol_failures > self.cfg.max_protocol_retries:
|
|
418
|
-
raise ProtocolError(
|
|
419
|
-
"The model returned an envelope with no tool calls and no final answer.",
|
|
420
|
-
last_reply,
|
|
421
|
-
)
|
|
422
491
|
if turn >= self.cfg.max_steps:
|
|
423
492
|
return self._wrap_up(turn, last_reply)
|
|
493
|
+
self.display.protocol_error(
|
|
494
|
+
"The envelope had neither tool_calls nor final."
|
|
495
|
+
)
|
|
424
496
|
message = build_protocol_repair(
|
|
425
497
|
"The envelope had neither tool_calls nor final.",
|
|
426
498
|
task=self.task,
|
|
@@ -701,6 +773,7 @@ def run_agent(
|
|
|
701
773
|
on_thinking_snapshot: Callable[[str], None] | None = None,
|
|
702
774
|
display: AgentDisplay | None = None,
|
|
703
775
|
history: ConversationLog | None = None,
|
|
776
|
+
resume: Compaction | None = None,
|
|
704
777
|
) -> AgentOutcome:
|
|
705
778
|
return Agent(
|
|
706
779
|
session=session,
|
|
@@ -716,4 +789,5 @@ def run_agent(
|
|
|
716
789
|
on_thinking_snapshot=on_thinking_snapshot,
|
|
717
790
|
display=display,
|
|
718
791
|
history=history,
|
|
792
|
+
resume=resume,
|
|
719
793
|
).run()
|