lcode-cli 0.2.1__tar.gz → 0.2.2__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 (68) hide show
  1. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/CHANGELOG.md +11 -1
  2. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/PKG-INFO +2 -2
  3. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/README.md +1 -1
  4. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/SECURITY.md +1 -1
  5. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/models.md +13 -2
  6. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/quickstart.md +1 -1
  7. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/usage.md +1 -2
  8. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/__init__.py +1 -1
  9. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/repl.py +113 -25
  10. lcode_cli-0.2.2/tests/test_context_picker.py +73 -0
  11. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.editorconfig +0 -0
  12. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/CODEOWNERS +0 -0
  13. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  14. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  15. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  16. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/ISSUE_TEMPLATE/model_request.yml +0 -0
  17. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  18. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/dependabot.yml +0 -0
  19. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/workflows/ci.yml +0 -0
  20. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/workflows/docs.yml +0 -0
  21. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.github/workflows/release.yml +0 -0
  22. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.gitignore +0 -0
  23. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/.pre-commit-config.yaml +0 -0
  24. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/AGENTS.md +0 -0
  25. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/CODE_OF_CONDUCT.md +0 -0
  26. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/CONTRIBUTING.md +0 -0
  27. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/LICENSE +0 -0
  28. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/assets/demo.svg +0 -0
  29. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/assets/extra.css +0 -0
  30. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/assets/logo.svg +0 -0
  31. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/assets/social-preview.png +0 -0
  32. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/changelog.md +0 -0
  33. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/configuration.md +0 -0
  34. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/contributing.md +0 -0
  35. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/how-it-works.md +0 -0
  36. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/index.md +0 -0
  37. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/installation.md +0 -0
  38. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/docs/troubleshooting.md +0 -0
  39. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/install.sh +0 -0
  40. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/mkdocs.yml +0 -0
  41. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/overrides/main.html +0 -0
  42. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/pyproject.toml +0 -0
  43. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/scripts/record_demo.py +0 -0
  44. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/scripts/social-preview.html +0 -0
  45. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/__main__.py +0 -0
  46. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/agent.py +0 -0
  47. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/catalog.py +0 -0
  48. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/cli.py +0 -0
  49. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/config.py +0 -0
  50. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/hardware.py +0 -0
  51. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/limits.py +0 -0
  52. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/models.toml +0 -0
  53. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/ollama.py +0 -0
  54. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/permissions.py +0 -0
  55. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/render.py +0 -0
  56. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/sessions.py +0 -0
  57. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/tools.py +0 -0
  58. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/src/lcode/web.py +0 -0
  59. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/conftest.py +0 -0
  60. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_agent.py +0 -0
  61. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_catalog.py +0 -0
  62. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_cli.py +0 -0
  63. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_config.py +0 -0
  64. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_permissions.py +0 -0
  65. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_sessions.py +0 -0
  66. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_tools.py +0 -0
  67. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/tests/test_web.py +0 -0
  68. {lcode_cli-0.2.1 → lcode_cli-0.2.2}/uv.lock +0 -0
@@ -6,6 +6,15 @@ All notable changes to lcode are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.2] - 2026-09-30
10
+
11
+ ### Added
12
+
13
+ - `/context` now lets you change the context window mid-session: it lists the sizes the model
14
+ supports with the memory each needs and whether it fits on the GPU, and you pick one (or type
15
+ `/context 128k`). If the conversation is too long for a smaller size, lcode summarizes it first.
16
+ `/ctx` is a shortcut for the same command.
17
+
9
18
  ## [0.2.1] - 2026-09-29
10
19
 
11
20
  ### Fixed
@@ -107,7 +116,8 @@ First public release.
107
116
  - `AGENTS.md` project instructions and `/init` to generate them.
108
117
  - One-line installer for Linux and macOS.
109
118
 
110
- [Unreleased]: https://github.com/nasser1941/lcode/compare/v0.2.1...HEAD
119
+ [Unreleased]: https://github.com/nasser1941/lcode/compare/v0.2.2...HEAD
120
+ [0.2.2]: https://github.com/nasser1941/lcode/compare/v0.2.1...v0.2.2
111
121
  [0.2.1]: https://github.com/nasser1941/lcode/compare/v0.2.0...v0.2.1
112
122
  [0.2.0]: https://github.com/nasser1941/lcode/compare/v0.1.2...v0.2.0
113
123
  [0.1.2]: https://github.com/nasser1941/lcode/compare/v0.1.1...v0.1.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lcode-cli
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A local-first terminal coding agent powered by open-weight models on your own GPU or Mac, via Ollama.
5
5
  Project-URL: Homepage, https://nasser1941.github.io/lcode/
6
6
  Project-URL: Documentation, https://nasser1941.github.io/lcode/
@@ -124,7 +124,7 @@ lcode
124
124
  | `lcode --model qwen3.5-9b --context 128k` | pick a model and context window for this session |
125
125
  | `lcode models` / `lcode doctor` | what fits this machine / check the installation |
126
126
  | `/rename`, `/resume` | name the current session, resume a saved one |
127
- | `/model`, `/ctx 128k`, `/compact`, `/help` | switch model, resize context, summarize, list commands |
127
+ | `/model`, `/context`, `/compact`, `/help` | switch model, resize the context window (pick from a list), summarize, list commands |
128
128
 
129
129
  ## Models
130
130
 
@@ -91,7 +91,7 @@ lcode
91
91
  | `lcode --model qwen3.5-9b --context 128k` | pick a model and context window for this session |
92
92
  | `lcode models` / `lcode doctor` | what fits this machine / check the installation |
93
93
  | `/rename`, `/resume` | name the current session, resume a saved one |
94
- | `/model`, `/ctx 128k`, `/compact`, `/help` | switch model, resize context, summarize, list commands |
94
+ | `/model`, `/context`, `/compact`, `/help` | switch model, resize the context window (pick from a list), summarize, list commands |
95
95
 
96
96
  ## Models
97
97
 
@@ -6,7 +6,7 @@ lcode is young; security fixes go into the latest release on `main`.
6
6
 
7
7
  | Version | Supported |
8
8
  |---|---|
9
- | 0.1.x (latest) | ✅ |
9
+ | 0.2.x (latest) | ✅ |
10
10
  | older | ❌ |
11
11
 
12
12
  ## Reporting a vulnerability
@@ -91,9 +91,20 @@ lcode --context 64k # this session only
91
91
  lcode config set context 128k # change the saved default
92
92
  ```
93
93
 
94
+ Inside a session, `/context` lists the sizes the model supports, with the memory each needs and
95
+ whether it fits on the GPU, and lets you pick one (or type `/context 128k` directly). The model
96
+ reloads with the new size on your next message; if the conversation is too long for a smaller size,
97
+ lcode summarizes it first.
98
+
94
99
  ```text
95
- ❯ /ctx 128k # inside a session (the model reloads on the next request)
96
- ❯ /ctx # show the current window and the model maximum
100
+ ❯ /context
101
+ Context window for lcode-qwen3.6-35b
102
+ # Size Memory · fit
103
+ 1 16K ~22 GB · GPU + RAM
104
+ 2 32K ~23 GB · GPU + RAM
105
+ …
106
+ 5 256K ~28 GB · GPU + RAM current, recommended
107
+ Choose a number, a size like 96k, or press Enter to keep 256K: 2
97
108
  ```
98
109
 
99
110
  Sizes accept `k` and `m` suffixes (`128k` = 131,072 tokens, `1m` = 1,048,576). When the window is
@@ -12,7 +12,7 @@ lcode
12
12
 
13
13
  ```text
14
14
  ╭──────────────────────────────────────────────╮
15
- │ lcode v0.2.1 — local coding agent │
15
+ │ lcode v0.2.2 — local coding agent │
16
16
  │ │
17
17
  │ model lcode-qwen3.6-35b │
18
18
  │ context 256K tokens │
@@ -50,8 +50,7 @@ reasoning is on.
50
50
  | `/rename NAME` | Name the current session so it's easy to find later |
51
51
  | `/resume [SESSION]` | Resume a saved session: pick from a list, or give its number or name; `/resume all` lists every folder |
52
52
  | `/compact [focus]` | Summarize the conversation to free context |
53
- | `/context` | Show how full the context window is |
54
- | `/ctx [size]` | Show or change the context window |
53
+ | `/context [size]` | Show how full the context window is and change its size: pick from a list with memory estimates, or give a size like `/context 128k` (`/ctx` is a shortcut) |
55
54
  | `/model [name]`, `/models` | Switch model, list models |
56
55
  | `/think [on\|off]` | Toggle reasoning |
57
56
  | `/verbose` | Toggle showing the reasoning text |
@@ -1,3 +1,3 @@
1
1
  """lcode — a local-first terminal coding agent powered by open-weight models via Ollama."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.2"
@@ -21,7 +21,7 @@ from rich.table import Table
21
21
  from rich.text import Text
22
22
 
23
23
  from lcode import __version__, catalog, limits, sessions, web
24
- from lcode.agent import INIT_PROMPT, Agent
24
+ from lcode.agent import AUTO_COMPACT_RATIO, INIT_PROMPT, Agent
25
25
  from lcode.catalog import MIN_USEFUL_CONTEXT
26
26
  from lcode.config import PERMISSION_MODES, STATE_DIR, ConfigError, format_tokens, parse_context
27
27
  from lcode.hardware import Hardware
@@ -35,8 +35,8 @@ COMMANDS = {
35
35
  "/rename": "Name this session so you can find it later, e.g. /rename auth refactor",
36
36
  "/resume": "Resume a saved session: pick from a list, or /resume <number|name> (/resume all: every folder)",
37
37
  "/compact": "Summarize the conversation to free context (optional: what to focus on)",
38
- "/context": "Show context-window usage",
39
- "/ctx": "Show or change the context window, e.g. /ctx 128k",
38
+ "/context": "Show context usage and change the window size: pick from a list, or /context 128k",
39
+ "/ctx": "Shortcut for /context",
40
40
  "/model": "Show or switch model, e.g. /model qwen3.5-9b",
41
41
  "/models": "List models and how they fit this machine",
42
42
  "/think": "Toggle model reasoning on/off",
@@ -145,6 +145,114 @@ def banner(agent: Agent) -> None:
145
145
  )
146
146
 
147
147
 
148
+ CONTEXT_CHOICES = (16384, 32768, 65536, 131072, 262144, 524288, 1048576)
149
+
150
+
151
+ def context_options(agent: Agent, hardware: Hardware) -> list[dict]:
152
+ """The context sizes this model supports, with how each one fits this machine."""
153
+ s = agent.settings
154
+ spec = catalog.find(s.model)
155
+ try:
156
+ max_ctx = agent.ollama.max_context(s.model) or (spec.max_context if spec else None)
157
+ except OllamaError:
158
+ max_ctx = spec.max_context if spec else None
159
+ sizes = {c for c in CONTEXT_CHOICES if not max_ctx or c <= max_ctx} | {s.context}
160
+ if max_ctx:
161
+ sizes.add(max_ctx)
162
+ learned = limits.get(s.model)
163
+ recommended = limits.cap(s.model, spec.fit(hardware)[0]) if spec and spec.fit(hardware)[0] else None
164
+ options = []
165
+ for size in sorted(sizes):
166
+ if spec:
167
+ memory = spec.memory_gib(size)
168
+ if hardware.unified:
169
+ fit = "fits" if memory <= hardware.budget_gib - catalog.HEADROOM_GIB else "too large"
170
+ elif hardware.gpu and memory <= hardware.vram_gib:
171
+ fit = "on GPU"
172
+ elif memory <= hardware.budget_gib - catalog.HEADROOM_GIB:
173
+ fit = "GPU + RAM" if spec.moe else "GPU + RAM, slow"
174
+ else:
175
+ fit = "too large"
176
+ note = f"~{memory:.0f} GB · {fit}"
177
+ else:
178
+ note = "no memory estimate for this model"
179
+ if learned and size > learned:
180
+ note += " · ran out of memory here before"
181
+ options.append({"size": size, "note": note, "current": size == s.context, "recommended": size == recommended})
182
+ return options
183
+
184
+
185
+ def context_command(agent: Agent, arg: str, hardware: Hardware) -> None:
186
+ """Show context usage and change the window size, from a list or directly (/context 128k)."""
187
+ c, s = agent.console, agent.settings
188
+ c.print(
189
+ f"In use: {format_tokens(agent.ctx_used)} of {format_tokens(s.context)} tokens "
190
+ f"({100 * agent.ctx_used / s.context:.0f}%) in {len(agent.messages)} message"
191
+ f"{'' if len(agent.messages) == 1 else 's'}; auto-compacts at 85%."
192
+ )
193
+ if arg:
194
+ try:
195
+ apply_context(agent, parse_context(arg), hardware)
196
+ except ConfigError as e:
197
+ c.print(f"[red]{e}[/]")
198
+ return
199
+ options = context_options(agent, hardware)
200
+ table = Table(title=f"Context window for {s.model}", title_justify="left", header_style="bold")
201
+ table.add_column("#", justify="right", style="cyan")
202
+ table.add_column("Size", justify="right")
203
+ table.add_column("Memory · fit")
204
+ table.add_column("")
205
+ for i, option in enumerate(options, 1):
206
+ marks = []
207
+ if option["current"]:
208
+ marks.append("[bold]current[/]")
209
+ if option["recommended"]:
210
+ marks.append("[cyan]recommended[/]")
211
+ table.add_row(str(i), format_tokens(option["size"]), option["note"], ", ".join(marks))
212
+ c.print(table)
213
+ try:
214
+ answer = input(f" Choose a number, a size like 96k, or press Enter to keep {format_tokens(s.context)}: ")
215
+ except EOFError:
216
+ return
217
+ answer = answer.strip()
218
+ if not answer:
219
+ c.print(f"Keeping {format_tokens(s.context)}.")
220
+ return
221
+ if answer.isdigit() and 1 <= int(answer) <= len(options):
222
+ size = options[int(answer) - 1]["size"]
223
+ else:
224
+ try:
225
+ size = parse_context(answer)
226
+ except ConfigError as e:
227
+ c.print(f"[red]{e}[/]")
228
+ return
229
+ apply_context(agent, size, hardware)
230
+
231
+
232
+ def apply_context(agent: Agent, size: int, hardware: Hardware) -> None:
233
+ c, s = agent.console, agent.settings
234
+ if size == s.context:
235
+ c.print(f"The context window is already {format_tokens(size)}.")
236
+ return
237
+ if agent.has_conversation() and agent.ctx_used > AUTO_COMPACT_RATIO * size:
238
+ c.print(
239
+ f"This conversation uses {format_tokens(agent.ctx_used)} tokens, too much for {format_tokens(size)}; "
240
+ "summarizing it first."
241
+ )
242
+ try:
243
+ agent.compact()
244
+ except OllamaError as e:
245
+ c.print(f"[red]Couldn't summarize the conversation, so the context stays the same: {e}[/]")
246
+ return
247
+ c.print(agent.set_context(size))
248
+ spec = catalog.find(s.model)
249
+ if spec and spec.memory_gib(s.context) > hardware.budget_gib:
250
+ c.print(
251
+ f"[yellow]~{spec.memory_gib(s.context):.0f} GB needed but ~{hardware.budget_gib:.0f} GB available; "
252
+ "this may run out of memory, in which case lcode falls back to a smaller size.[/]"
253
+ )
254
+
255
+
148
256
  def web_status(agent: Agent) -> str:
149
257
  if agent.settings.web == "off":
150
258
  return "off"
@@ -196,28 +304,8 @@ def handle_command(agent: Agent, line: str, hardware: Hardware) -> bool:
196
304
  resume_session(agent, info)
197
305
  elif cmd == "/compact":
198
306
  agent.compact(arg)
199
- elif cmd == "/context":
200
- c.print(
201
- f"Context: {format_tokens(agent.ctx_used)} of {format_tokens(s.context)} tokens "
202
- f"({100 * agent.ctx_used / s.context:.1f}%) in {len(agent.messages)} messages; "
203
- "auto-compacts at 85%."
204
- )
205
- elif cmd == "/ctx":
206
- if not arg:
207
- spec = catalog.find(s.model)
208
- extra = f" · model maximum {format_tokens(spec.max_context)}" if spec else ""
209
- c.print(f"Context window: {format_tokens(s.context)} tokens{extra}. Change it with /ctx 128k")
210
- else:
211
- try:
212
- c.print(agent.set_context(parse_context(arg)))
213
- except ConfigError as e:
214
- c.print(f"[red]{e}[/]")
215
- spec = catalog.find(s.model)
216
- if spec and spec.memory_gib(s.context) > hardware.budget_gib:
217
- c.print(
218
- f"[yellow]~{spec.memory_gib(s.context):.0f} GB needed but ~{hardware.budget_gib:.0f} GB "
219
- "available; this may run out of memory.[/]"
220
- )
307
+ elif cmd in ("/context", "/ctx"):
308
+ context_command(agent, arg, hardware)
221
309
  elif cmd == "/models":
222
310
  print_models(agent.ollama, hardware, s.model)
223
311
  elif cmd == "/model":
@@ -0,0 +1,73 @@
1
+ import pytest
2
+
3
+ from conftest import output, reply
4
+ from lcode import limits
5
+ from lcode.hardware import Hardware
6
+ from lcode.repl import context_options, handle_command
7
+
8
+ LAPTOP = Hardware("linux", "x", 31, "RTX 4080 Laptop", 12)
9
+
10
+
11
+ def answer(monkeypatch, text):
12
+ monkeypatch.setattr("builtins.input", lambda _: text)
13
+
14
+
15
+ def test_options_show_fit_current_and_recommended(agent):
16
+ options = {o["size"]: o for o in context_options(agent, LAPTOP)}
17
+ assert sorted(options) == [16384, 32768, 65536, 131072, 262144] # capped at the model's 256K
18
+ assert options[65536]["current"] and options[262144]["recommended"]
19
+ assert options[262144]["note"] == "~28 GB · GPU + RAM"
20
+
21
+
22
+ def test_options_warn_about_sizes_that_ran_out_of_memory(agent):
23
+ limits.record(agent.settings.model, 131072)
24
+ options = {o["size"]: o for o in context_options(agent, LAPTOP)}
25
+ assert "ran out of memory here before" in options[262144]["note"]
26
+ assert "ran out of memory" not in options[131072]["note"]
27
+ assert options[131072]["recommended"]
28
+
29
+
30
+ def test_options_for_models_outside_the_catalog(make_agent):
31
+ agent = make_agent(model="custom:7b", context=8192)
32
+ options = context_options(agent, LAPTOP)
33
+ assert options[0]["size"] == 8192 and options[0]["current"]
34
+ assert options[0]["note"] == "no memory estimate for this model"
35
+
36
+
37
+ def test_pick_by_number(agent, monkeypatch):
38
+ answer(monkeypatch, "4") # 16K, 32K, 64K, 128K, ...
39
+ handle_command(agent, "/context", LAPTOP)
40
+ assert agent.settings.context == 131072
41
+ assert "Context window set to 128K" in output(agent)
42
+
43
+
44
+ def test_pick_by_size_and_keep(agent, monkeypatch):
45
+ answer(monkeypatch, "96k")
46
+ handle_command(agent, "/context", LAPTOP)
47
+ assert agent.settings.context == 98304
48
+ answer(monkeypatch, "")
49
+ handle_command(agent, "/ctx", LAPTOP)
50
+ assert agent.settings.context == 98304 and "Keeping 96K" in output(agent)
51
+
52
+
53
+ def test_bad_answer_changes_nothing(agent, monkeypatch):
54
+ answer(monkeypatch, "lots")
55
+ handle_command(agent, "/context", LAPTOP)
56
+ assert agent.settings.context == 65536 and "invalid context size" in output(agent)
57
+
58
+
59
+ def test_direct_size_skips_the_list(agent, monkeypatch):
60
+ monkeypatch.setattr("builtins.input", lambda _: pytest.fail("should not ask"))
61
+ handle_command(agent, "/context 32k", LAPTOP)
62
+ assert agent.settings.context == 32768
63
+
64
+
65
+ def test_shrinking_below_the_conversation_summarizes_first(make_agent, monkeypatch):
66
+ agent = make_agent([reply("first answer"), reply("SUMMARY of the work")])
67
+ agent.run_turn("a long task")
68
+ agent.ctx_used = 60000 # more than 85% of 32K
69
+ monkeypatch.setattr("builtins.input", lambda _: pytest.fail("should not ask"))
70
+ handle_command(agent, "/context 32k", LAPTOP)
71
+ assert agent.settings.context == 32768
72
+ assert "SUMMARY of the work" in agent.messages[1]["content"]
73
+ assert "summarizing it first" in output(agent)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes