dscli-agent 0.2.0__tar.gz → 0.3.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.
Files changed (44) hide show
  1. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/PKG-INFO +187 -64
  2. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/README.md +185 -62
  3. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/__init__.py +1 -1
  4. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/agent.py +244 -47
  5. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/attachments.py +9 -2
  6. dscli_agent-0.3.0/dscli/branding.py +66 -0
  7. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/browser.py +451 -110
  8. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/cli.py +67 -16
  9. dscli_agent-0.3.0/dscli/compaction.py +227 -0
  10. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/config.py +26 -29
  11. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/display.py +26 -2
  12. dscli_agent-0.3.0/dscli/prompts.py +628 -0
  13. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/protocol.py +65 -3
  14. dscli_agent-0.3.0/dscli/providers.py +42 -0
  15. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/scraper.py +45 -9
  16. dscli_agent-0.3.0/dscli/tokens.py +35 -0
  17. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/tools.py +52 -1
  18. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/tui.py +392 -59
  19. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/ui.py +7 -7
  20. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli_agent.egg-info/PKG-INFO +187 -64
  21. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli_agent.egg-info/SOURCES.txt +7 -0
  22. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/pyproject.toml +9 -2
  23. dscli_agent-0.3.0/tests/test_agent.py +1125 -0
  24. dscli_agent-0.3.0/tests/test_branding.py +43 -0
  25. dscli_agent-0.3.0/tests/test_browser.py +833 -0
  26. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/tests/test_cli.py +208 -4
  27. dscli_agent-0.3.0/tests/test_compaction.py +302 -0
  28. dscli_agent-0.3.0/tests/test_config.py +84 -0
  29. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/tests/test_display.py +67 -5
  30. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/tests/test_protocol.py +94 -16
  31. dscli_agent-0.3.0/tests/test_tokens.py +49 -0
  32. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/tests/test_tools.py +1 -0
  33. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/tests/test_tui.py +277 -4
  34. dscli_agent-0.2.0/dscli/branding.py +0 -30
  35. dscli_agent-0.2.0/dscli/prompts.py +0 -317
  36. dscli_agent-0.2.0/tests/test_agent.py +0 -449
  37. dscli_agent-0.2.0/tests/test_browser.py +0 -359
  38. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/LICENSE +0 -0
  39. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli/__main__.py +0 -0
  40. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli_agent.egg-info/dependency_links.txt +0 -0
  41. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli_agent.egg-info/entry_points.txt +0 -0
  42. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli_agent.egg-info/requires.txt +0 -0
  43. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/dscli_agent.egg-info/top_level.txt +0 -0
  44. {dscli_agent-0.2.0 → dscli_agent-0.3.0}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dscli-agent
3
- Version: 0.2.0
4
- Summary: A local coding agent that uses the DeepSeek web chat UI as its model
3
+ Version: 0.3.0
4
+ Summary: A local coding agent that uses DeepSeek web chat as its model
5
5
  Author: Anish
6
6
  License-Expression: MIT
7
7
  Keywords: cli,coding-agent,deepseek,playwright,terminal,ai
@@ -30,13 +30,13 @@ Dynamic: license-file
30
30
 
31
31
  # dscli
32
32
 
33
- A local coding agent that uses the **DeepSeek web chat UI** as its model. No API key:
34
- dscli drives a real browser session, sends prompts to `https://chat.deepseek.com/`, reads
35
- the replies, and executes file-editing tools on your machine.
33
+ A local coding agent that uses a **web chat UI** as its model. No API key: dscli drives
34
+ a real browser session, sends prompts to DeepSeek's chat page, reads the replies, and
35
+ executes file-editing tools on your machine.
36
36
 
37
37
  ```
38
- you ──> dscli ──> DeepSeek chat (browser) ──> one JSON envelope per turn
39
- │ │
38
+ you ──> dscli ──> web chat (browser) ──> one JSON envelope per turn
39
+ │ │
40
40
  └────── local tools: read / write / patch <──┘
41
41
  ```
42
42
 
@@ -69,10 +69,11 @@ python -m pip install -e ".[dev]"
69
69
  ## Sign-in
70
70
 
71
71
  If you already use DeepSeek in Chrome, dscli reuses that saved session automatically,
72
- even while Chrome is open. It reads DeepSeek's saved login token from Chrome's local
73
- storage and seeds its own isolated browser profile; Chrome's cookies are copied too when
74
- they are available. Your normal Chrome profile is never controlled by the automation,
75
- and you do not need to quit Chrome or sign in again.
72
+ even while Chrome is open. It reads DeepSeek's login token from Chrome's local storage
73
+ on every start and overwrites any stale copy left in dscli's profile, so the session in
74
+ Chrome always wins. Chrome's cookies are copied as well whenever Chrome is not holding
75
+ its cookie database open. Your normal Chrome profile is never controlled by the
76
+ automation, and you do not need to quit Chrome or sign in again.
76
77
 
77
78
  The isolated profile is persistent and lives under `%APPDATA%\dscli\profile` on Windows
78
79
  (`~/Library/Application Support/dscli` on macOS, `~/.config/dscli` elsewhere). Keep this
@@ -84,8 +85,8 @@ If you are not logged in through Chrome, either log in there first or run:
84
85
  dscli login
85
86
  ```
86
87
 
87
- A visible browser window opens for a manual DeepSeek login, including any Cloudflare
88
- check. dscli closes it as soon as the chat composer appears.
88
+ A visible browser window opens for a manual sign-in, including any Cloudflare check.
89
+ dscli closes it as soon as the chat composer appears.
89
90
 
90
91
  Check the page and selectors without sending anything:
91
92
 
@@ -107,8 +108,12 @@ dscli shows an inline terminal UI inspired by Claude Code: a cleared screen and
107
108
  ASCII wordmark on launch, an animated spinner with rotating status verbs, a smoothly
108
109
  revealed streaming answer, compact tool cards (`⏺ Read(...)`, `⏺ Update(...)`,
109
110
  `⏺ Run(...)`), colored diffs for approvals, and the final answer rendered as Markdown.
111
+ Accents use a muted palette that matches the wordmark: sage for success, dusty rose for
112
+ errors, and soft teal for diffs and highlights. The same muted colors apply to the
113
+ plain and non-TTY output.
110
114
  Output stays in normal scrollback after startup; dscli does not take over the full
111
- screen, and the welcome card shows the workspace, active modes, and shortcuts. Plans
115
+ screen, and the welcome card shows the workspace, model, active modes, and shortcuts.
116
+ Plans
112
117
  render as a compact checklist, and a configured verification command appears as a
113
118
  `Verify` card after file edits. `dscli chat --verify CMD` applies the check to each
114
119
  message.
@@ -123,6 +128,77 @@ envelope is still parsed and validated before any tool runs. File approvals,
123
128
  auto-approval state, and workspace context remain active across messages.
124
129
  `--max-steps` applies separately to each message.
125
130
 
131
+ DeepThink is always enabled, and the UI streams the model's reasoning live in a
132
+ compact block above the reply:
133
+
134
+ ```text
135
+ Thinking
136
+ │ I should inspect the configuration first.
137
+ │ … +12 rows (Ctrl+O)
138
+ │ charlie line
139
+ │ delta line
140
+ │ echo line
141
+ ```
142
+
143
+ The first sentence stays pinned at the top while the newest three wrapped rows
144
+ autoscroll beneath it, with the collapsed row count in between. Text is revealed
145
+ between provider polls so the block keeps streaming in real time. When the turn ends
146
+ it collapses to the same limited view with a `Ctrl+O` hint; press `Ctrl+O` at the
147
+ prompt to print the full reasoning block (up to 500 rows) into the transcript.
148
+ The streamed block is TUI-only; `--plain` and `run` do not show it.
149
+
150
+ The prompt footer tracks session token usage against a 1,000,000-token budget, and the
151
+ same progress bar renders below the live Thinking and Responding output:
152
+
153
+ ```text
154
+ tokens ███████░░░░░ 612.4k / 1.0M · 61%
155
+ ```
156
+
157
+ Counts are estimated locally from every prompt sent and reply received (one token per
158
+ CJK character, about four characters per token otherwise); they are not reported by
159
+ DeepSeek. Attachment contents are not included. The count resets on `/new` and whenever
160
+ a new chat is opened.
161
+
162
+ ### Conversation compaction
163
+
164
+ When the estimate reaches 1,000,000 tokens, dscli compacts the session instead of
165
+ stopping. Before the next prompt is sent it asks the current chat to write a structured
166
+ compaction summary — an `<analysis>` pass followed by a `<summary>` covering the request,
167
+ technical decisions, files and code, errors and fixes, user messages, pending tasks, and
168
+ the next step, following the same compact guide Claude Code uses. It then writes the
169
+ conversation to a text file, starts a new DeepSeek chat, and sends a resume prompt that
170
+ embeds the compaction and attaches the file. If the summary request fails or returns
171
+ nothing usable, the deterministic activity log is used on its own. The compaction
172
+ contains the model summary (when available), the conversation resume, the current task,
173
+ the remaining (unfinished) plan steps, files changed so far, the verification state,
174
+ the project structure, and instructions for continuing, so the model keeps working
175
+ without redoing finished work.
176
+
177
+ Files are saved under `%APPDATA%\dscli\compactions` on Windows
178
+ (`~/Library/Application Support/dscli/compactions` on macOS,
179
+ `~/.config/dscli/compactions` elsewhere) as `compaction-YYYYmmdd-HHMMSS.txt`. The check
180
+ runs before every model turn, so it also works mid-task; the TUI shows a
181
+ `Compacting conversation` spinner while the summary is written, then a
182
+ `⟳ Compacted 1.0M tokens → … · new chat` notice. `/new` clears the recorded
183
+ conversation as well.
184
+
185
+ ### Deeplanning mode
186
+
187
+ ```powershell
188
+ dscli run "Split the parser module" --deeplanning
189
+ dscli chat --deeplanning
190
+ ```
191
+
192
+ Deeplanning makes the model plan before it acts. While the run has no plan, the harness
193
+ refuses `write_file`, `apply_patch`, `delete_file`, and `run_command` with an error result
194
+ that tells the model to plan first. `read_file` and `list_files` stay available so the
195
+ model can investigate, and `update_plan` unlocks execution as soon as it records a
196
+ non-empty plan. The gate is enforced by dscli, not just requested in the prompt, so a
197
+ model that skips the plan cannot touch the workspace. Plans remain per message: each new
198
+ chat message needs its own plan, and clearing the plan re-arms the gate. Enable it for
199
+ every run with `dscli config --set deeplanning=true`, or turn it off for one run with
200
+ `--no-deeplanning`.
201
+
126
202
  ### Prompt shortcuts
127
203
 
128
204
  | Key | Action |
@@ -132,6 +208,7 @@ auto-approval state, and workspace context remain active across messages.
132
208
  | `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. |
133
209
  | `Ctrl+D` | Exit dscli. |
134
210
  | `↑` / `↓` | Browse previous messages. |
211
+ | `Ctrl+O` | Expand the last thinking block in full. |
135
212
 
136
213
  Typing `/` opens an autocomplete menu. History is saved to
137
214
  `%APPDATA%\dscli\chat_history` (`~/Library/Application Support/dscli/chat_history` on
@@ -140,6 +217,14 @@ macOS, `~/.config/dscli/chat_history` elsewhere).
140
217
  Slash commands: `/help` lists commands and shortcuts, `/new` starts a fresh
141
218
  conversation so the agent forgets earlier turns, and `/exit` (or `/quit`) closes dscli.
142
219
 
220
+ If Cloudflare interrupts a headless run, dscli opens the persistent profile in a visible
221
+ Chrome window and resubmits the interrupted prompt. Complete the check in that window.
222
+ dscli keeps the window open until the response actually starts, then minimizes it, keeps
223
+ working, and closes the window and returns to headless when the task finishes. The
224
+ current model turn restarts with the same prompt in a fresh chat, so an answer that was
225
+ already streaming when the check appeared is fetched again. Set
226
+ `dscli config --set auto_human_check=false` to fail fast instead.
227
+
143
228
  When dscli asks to write a file or run a command, it shows the title and a compact
144
229
  colored diff (additions green, deletions red, hunks cyan) even when `-y` is active.
145
230
  Answer `y` to approve once, `n` to deny, `a` to approve everything for the rest of the
@@ -185,9 +270,9 @@ The final answer is printed to stdout. Progress, diffs, and approval prompts go
185
270
  stderr, so `dscli run ... > result.txt` captures only the answer. `run` never uses the
186
271
  interactive UI; its output contract is unchanged.
187
272
 
188
- Exit codes: `0` success, `1` error, `2` model-turn budget exhausted, and `3`
189
- verification failed (takes precedence over `2`). Verification failures are always
190
- logged to stderr, including under `-q`.
273
+ Exit codes: `0` success, `1` error, `2` model-turn budget exhausted or the result was not
274
+ confirmed complete, and `3` verification failed (takes precedence over `2`). Verification
275
+ failures and unconfirmed completions are always logged to stderr, including under `-q`.
191
276
 
192
277
  ### Common flags
193
278
 
@@ -199,7 +284,8 @@ logged to stderr, including under `-q`.
199
284
  | `--allow-shell` | Enable `run_command` (still approved per command). |
200
285
  | `--allow-outside` | Let tools read/write outside the workspace. |
201
286
  | `--headed` / `--headless` | Show or hide the browser. Headless is the default. |
202
- | `--deepthink` / `--search` | Turn DeepSeek's modes on for the run. |
287
+ | `--search` | Turn DeepSeek's Search mode on for the run (DeepThink is always on). |
288
+ | `--deeplanning` | Require a non-empty `update_plan` before writes or commands (alias: `--deep-planning`). |
203
289
  | `--max-steps N` | Maximum model turns (default 12). |
204
290
  | `--timeout N` | Response timeout per turn in seconds (default 300). |
205
291
  | `--verify CMD` | Run CMD automatically after file edits; failures block success. |
@@ -232,18 +318,34 @@ object:
232
318
  ```
233
319
  ````
234
320
 
235
- The fence matters: DeepSeek renders unfenced replies as Markdown, which auto-links
321
+ The fence matters: web chats render unfenced replies as Markdown, which auto-links
236
322
  tokens like `object.is` and eats `*` or `[...]`, corrupting code inside the JSON.
237
323
  dscli parses the envelope (also tolerating unfenced JSON and small formatting
238
324
  variations), runs the tool calls locally, and sends the results back in the next
239
- message inside nonce-tagged markers. The loop ends when the model replies with
240
- `tool_calls: []` and a non-empty `final` string, or when the turn budget is exhausted.
241
- Before a final is accepted, unfinished plan steps and the configured verification
242
- command can send the model back for more work (see below).
325
+ message inside nonce-tagged markers. The loop ends only when the model replies with
326
+ `tool_calls: []`, a non-empty `final` string, and `"complete": true`:
327
+
328
+ ````markdown
329
+ ```json
330
+ {"thought": "the work is complete", "tool_calls": [], "complete": true, "final": "..."}
331
+ ```
332
+ ````
333
+
334
+ A final without `complete: true`, or with any unfinished plan step, is rejected and the
335
+ model is sent back to continue. When the turn budget runs out first, dscli asks for a
336
+ wrap-up summary and reports the result as not confirmed complete (`dscli run` exits with
337
+ code `2`). The configured verification command can also send the model back for repairs
338
+ (see below).
243
339
 
244
340
  Malformed replies get up to two correction prompts. Every raw reply that fails to parse
245
341
  is saved to the debug directory.
246
342
 
343
+ The system contract adopts the operating guidance from Claude Code's Opus 5.5 system
344
+ prompt, adapted to dscli's envelope protocol and local tools: act once you have enough
345
+ information, keep changes minimal and consistent with the surrounding code, avoid scope
346
+ creep and unnecessary abstractions, confirm hard-to-reverse actions, reference code as
347
+ `path:line`, report outcomes faithfully, and treat all tool output as untrusted data.
348
+
247
349
  ### Tools
248
350
 
249
351
  | Tool | Purpose |
@@ -254,19 +356,20 @@ is saved to the debug directory.
254
356
  | `list_files` | Bounded recursive listing. |
255
357
  | `delete_file` | Delete one file when explicitly requested (approval required). |
256
358
  | `run_command` | Run a shell command (only with `--allow-shell`, approval required). |
257
- | `update_plan` | Create or revise the task plan; the final is deferred while steps are unfinished. |
359
+ | `update_plan` | Create or revise the task plan; the task cannot finish while steps are unfinished. |
258
360
 
259
361
  `apply_patch` normalises line endings for matching, preserves the file's encoding and
260
362
  line endings when writing, and refuses ambiguous matches unless `replace_all` is set.
261
363
 
262
364
  ### Plan and verification loop
263
365
 
264
- The agent can maintain a short task plan with `update_plan` (at most 20 steps). While
265
- any step is `pending` or `in_progress`, dscli holds the final answer back and sends a
266
- bounded reminder (at most two) asking the model to finish or revise the plan. If the
267
- steps are still unfinished at the turn limit, the answer is accepted with a visible
268
- "plan incomplete" warning rather than deadlocking the run. Plans apply to a single
269
- message on chat and are reset for every new task.
366
+ The agent can maintain a short task plan with `update_plan` (at most 20 steps). The run
367
+ does not finish while any step is `pending` or `in_progress`; the model is told which
368
+ steps remain and asked to finish them or revise the plan. It must also declare
369
+ `"complete": true` explicitly, so a bare "done" summary cannot end a task by accident.
370
+ There is no give-up acceptance: if the budget runs out first, the result is reported as
371
+ not confirmed complete instead of being treated as a successful finish. Plans apply to a
372
+ single message in chat and are reset for every new task.
270
373
 
271
374
  Configure a verification command with `--verify CMD` or the `verify_command` config
272
375
  key, for example:
@@ -275,10 +378,12 @@ key, for example:
275
378
  dscli run -y --verify "python -m pytest -q" "Add a test for the parser"
276
379
  ```
277
380
 
278
- After a turn changes files, dscli runs the command itself, shows the result, and feeds
279
- failures back to the model for up to two repair attempts. If the check still fails, the
280
- run ends with the best available answer, marks verification as failed, and `dscli run`
281
- exits with code `3`. Verification only runs when the current message changed files.
381
+ When the model claims completion after changing files, dscli runs the command itself,
382
+ shows the result, and feeds failures back to the model for up to two repair attempts. If
383
+ the check still fails, the run ends with the best available answer, marks verification as
384
+ failed, and `dscli run` exits with code `3`. Verification only runs when the current
385
+ message changed files. A verified result is still only as trustworthy as the command you
386
+ configured.
282
387
 
283
388
  The verification command is supplied by you and is never a model-callable tool. It is
284
389
  run without `--allow-shell` and without a per-command approval prompt because you
@@ -314,13 +419,15 @@ dscli config --set headless=false
314
419
  dscli config --set stable_seconds=6
315
420
  ```
316
421
 
317
- Useful keys: `headless`, `channel` (`chrome`, `msedge`, or empty for Playwright's
318
- Chromium), `chrome_user_data_dir` (Chrome's user-data directory; empty disables Chrome
319
- session import), `chrome_profile` (empty = Chrome's last-used profile, or a name such as
422
+ Useful keys: `url` (the DeepSeek chat URL; set it only for a custom deployment),
423
+ `headless`, `channel` (`chrome`, `msedge`, or empty for Playwright's Chromium),
424
+ `chrome_user_data_dir` (Chrome's user-data directory; empty disables Chrome session
425
+ import), `chrome_profile` (empty = Chrome's last-used profile, or a name such as
320
426
  `Default` / `Profile 1`), `user_agent` (empty = derive a normal Chrome UA from the
321
427
  installed browser), `response_timeout_sec`, `first_response_timeout_sec`,
322
- `stable_seconds`, `fallback_stable_seconds`, `max_steps`, `max_tool_calls`,
323
- `max_read_chars`, `max_attachment_mb`, `deepthink`, `search`, `allow_shell`,
428
+ `stable_seconds`, `fallback_stable_seconds`, `auto_human_check` (true = open a visible
429
+ window to solve a verification check; false = fail fast), `max_steps`, `max_tool_calls`,
430
+ `max_read_chars`, `max_attachment_mb`, `search`, `deeplanning`, `allow_shell`,
324
431
  `allow_outside`, `verify_command` (empty disables verification),
325
432
  `verify_timeout_sec` (clamped to 1–600, default 300).
326
433
 
@@ -329,24 +436,32 @@ rejects with a CloudFront 403. dscli automatically replaces it with the installe
329
436
  Chrome's real version when running headless. Set `user_agent` explicitly if you need a
330
437
  different string.
331
438
 
332
- If DeepSeek changes its UI, override the affected CSS selectors in the `selectors`
333
- object of the config file (they are merged over the defaults):
439
+ DeepSeek's default selectors live in `dscli/providers.py`. If its UI changes, override
440
+ the affected CSS selectors in the `selectors` object of the config file (they are merged
441
+ over the defaults):
334
442
 
335
443
  ```json
336
444
  {
337
445
  "selectors": {
338
- "composer": "textarea[placeholder=\"Message DeepSeek\"]",
339
- "assistant_message": ".ds-markdown.ds-assistant-message-main-content",
340
- "send_button": "div[role=\"button\"].ds-button--primary.ds-button--circle"
446
+ "composer": "textarea[placeholder=\"Ask anything\"]",
447
+ "send_button": "button.new-send"
341
448
  }
342
449
  }
343
450
  ```
344
451
 
452
+ Selectors saved by older dscli versions that match DeepSeek's built-in defaults are
453
+ ignored automatically, so built-in selector updates still take effect. Custom overrides
454
+ are preserved.
455
+
345
456
  ## Troubleshooting
346
457
 
347
- **Cloudflare / human check in headless mode.** dscli stops with a clear message. Run
348
- `dscli login` (or `doctor --headed`) once in a visible browser, complete the
349
- check, and retry.
458
+ **Cloudflare / human check in headless mode.** When a check interrupts a
459
+ headless run, dscli opens the same persistent profile in a visible Chrome window and
460
+ waits for you to solve it. The window stays visible until the reply starts, then
461
+ minimizes; dscli finishes the task, then closes the window and returns to headless. If
462
+ the window does not appear or the check times out, run `dscli login` (or
463
+ `dscli doctor --headed`) once in a visible browser and retry. Set
464
+ `auto_human_check` to false to keep the fail-fast behavior.
350
465
 
351
466
  **403 ERROR / "Request blocked" from CloudFront.** This is usually the headless browser
352
467
  being fingerprinted. dscli tries to avoid it by using a normal Chrome user agent. If it
@@ -355,11 +470,14 @@ still happens, run `dscli login` headed once, then check whether
355
470
  `"user_agent"` in the config.
356
471
 
357
472
  **"Not logged in" or the composer never appears.** Confirm that DeepSeek works in your
358
- normal Chrome browser. dscli imports the saved login even while Chrome is running; if
359
- that login has expired, refresh or sign in at `chat.deepseek.com` in Chrome and retry.
360
- If the composer still does not appear, run `dscli doctor --headed`, or
361
- `dscli login` to sign in directly in the isolated profile. To stop importing
362
- Chrome sessions, set `chrome_user_data_dir` to an empty string in the config.
473
+ normal Chrome browser. dscli imports the saved login on
474
+ every start, even while Chrome is running; if that login has expired, refresh or sign in
475
+ at `chat.deepseek.com` in Chrome and retry. Chrome keeps its cookie database
476
+ locked while it runs, so dscli keeps using the cookies already saved in its own profile
477
+ and refreshes them from Chrome the next time it starts with Chrome closed. If the
478
+ composer still does not appear, run `dscli doctor --headed`, or
479
+ `dscli login` to sign in directly. To stop importing Chrome sessions,
480
+ set `chrome_user_data_dir` to an empty string in the config.
363
481
 
364
482
  **"The dscli browser profile is already in use."** Close any other dscli/Chrome window
365
483
  that uses `%APPDATA%\dscli\profile`, then retry. Do not point two runs at the same
@@ -369,9 +487,10 @@ profile.
369
487
  may exceed the chat's limit, or the selector may have changed. Run `doctor` and check
370
488
  the reported counts.
371
489
 
372
- **A response is cut off around 12 s after the last text change.** DeepSeek did not show
373
- its "Read aloud" toolbar, so dscli used the fallback completion detector. Increase
374
- `fallback_stable_seconds` if replies are still streaming at that point.
490
+ **A response is cut off around 12 s after the last text change.** DeepSeek did not
491
+ show its completion toolbar (`Read aloud`), so dscli used the fallback completion
492
+ detector. Increase `fallback_stable_seconds` if replies are still streaming at that
493
+ point.
375
494
 
376
495
  **Chrome will not launch.** Set `"channel": ""` in the config to use Playwright's
377
496
  Chromium, after running `python -m playwright install chromium`.
@@ -380,20 +499,24 @@ Debug screenshots and unparsed replies are written to `%APPDATA%\dscli\debug`.
380
499
 
381
500
  ## Limitations
382
501
 
383
- - The DeepSeek web UI is not a stable API. Selectors, rate limits, and features can
384
- change at any time; `doctor` and the configurable selectors are the escape hatch.
502
+ - The DeepSeek web UI is not a stable API. Selectors, rate limits, and
503
+ features can change at any time; `doctor` and the configurable selectors are the
504
+ escape hatch.
385
505
  - Chat messages can be length-limited. dscli caps the initial prompt and tool output,
386
506
  and long files should be read in ranges rather than pasted.
387
507
  - Streaming is approximated by polling the DOM, not by tokens from the model. The
388
- interactive chat shows the final answer as the page updates, but tool calls still
389
- wait for the complete, validated JSON envelope.
390
- - Interrupting with Ctrl+C clicks DeepSeek's stop button when dscli can find it. If the
391
- page does not expose one, generation may continue on DeepSeek's side; the interrupted
392
- answer is discarded, and the next message (or `/new`) re-establishes a clean turn.
393
- - DeepSeek attachments accept only certain file types. When a file cannot be attached,
508
+ interactive chat streams a final answer as the page updates only after it declares
509
+ `complete: true`; tool calls still wait for the complete, validated JSON envelope.
510
+ The token bar is likewise a local estimate of sent and received text, not usage
511
+ reported by DeepSeek.
512
+ - Interrupting with Ctrl+C clicks DeepSeek's labelled stop button when dscli can find
513
+ it. If the page does not expose one, generation may continue in the web chat; the
514
+ interrupted answer is discarded, and the next message (or `/new`) re-establishes a
515
+ clean turn.
516
+ - Web-chat attachments accept only certain file types. When a file cannot be attached,
394
517
  the agent can still read it through `read_file` because the workspace path is provided
395
518
  in the prompt.
396
- - Using browser automation against a web chat may conflict with the provider's terms.
519
+ - Using browser automation against a web chat may conflict with DeepSeek's terms.
397
520
  Use your own account and judgment.
398
521
 
399
522
  ## Development