Skip to content
This repository was archived by the owner on Jun 3, 2026. It is now read-only.

Commit a1c7196

Browse files
committed
feat: Release v1.9.0 - Accuracy, Context, and Interaction Overhaul
- Native Token Counting: Integrated Gemini countTokens API for 100% accuracy. - Proactive Context: Automatic 90% threshold pruning with background summarization. - Deep Reasoning: Native support for reasoning_content (DeepSeek/OpenRouter) and configurable UI transparency. - Interactive Queue: New 'unroll-to-edit' logic for pending messages. - Session Isolation: transitioned scratchpad to session-scoped storage. - UI & Stability: Hardened background workers and thread-safe modal handling.
1 parent 4de6e7a commit a1c7196

22 files changed

Lines changed: 786 additions & 150 deletions

README.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,40 @@
44

55
![Plexir UI](assets/image.png)
66

7-
[![Version](https://img.shields.io/badge/version-1.8.0-blue.svg)](https://github.com/pomilon/plexir)
7+
[![Version](https://img.shields.io/badge/version-1.9.0-blue.svg)](https://github.com/pomilon/plexir)
88
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
99

1010
---
1111

1212
## Features
1313

1414
- **Multi-Provider Failover**: Seamlessly switch between Gemini, Groq, Cerebras, and OpenAI-compatible APIs. If one model hits a quota, Plexir automatically fails over to the next in your priority list.
15-
- **Economics & Metrics**: Real-time **Token Tracking** and **Cost Estimation** in the sidebar. Set a session budget via `/config budget` to prevent runaway costs.
16-
- **Advanced Reasoning Support**: Automatically filters model "thinking" blocks into collapsible widgets and provides a **Live Status Spinner** during reasoning.
15+
- **Accuracy & Economics**:
16+
- **Native Token Counting**: Integrated Gemini native token counting API for 100% accurate measurement.
17+
- **Proactive Context Management**: Automatic pruning/summarization when context reaches 90% capacity to prevent truncation errors.
18+
- **Cost Estimation**: Real-time tracking in the sidebar. Set a session budget via `/config budget`.
19+
- **Deep Reasoning Support**: Native support for `reasoning_content` (DeepSeek/OpenRouter) with configurable transparency (toggle blocks with `/config reasoning`).
20+
- **Responsive Interaction**:
21+
- **Message Queuing**: Submit messages while the AI is busy; they appear in a "queued" state and process sequentially.
22+
- **Interactive Queue Management**: Click a queued message to "unroll" the queue and pull messages back to the input for editing.
1723
- **Coherent Memory**:
1824
- **Persistent Memory Bank**: Semantic storage (`chromadb`) for long-term facts using `/memory save`.
25+
- **Session-Scoped Scratchpad**: Isolated planning space (`scratchpad` tool) that persists within a session but doesn't pollute global history.
1926
- **Rolling Summarization**: Automatically condenses long histories.
2027
- **Message Pinning**: `/session pin` ensures critical context is never lost.
21-
- **Context Window Management**: Intelligent token counting and history pruning ensures conversations never exceed model limits, preventing failover loops.
2228
- **Persistent Docker Sandbox**: Launch with `--sandbox` to give the AI its own persistent Linux "computer." All tools (file system, git, shell) are automatically redirected inside the container.
23-
- **Deep MCP Integration**: Fully supports **Model Context Protocol (MCP)**, including dynamic discovery of tools, **Resources**, **Resource Templates**, and **Prompts** from MCP servers.
29+
- **Deep MCP Integration**: Fully supports **Model Context Protocol (MCP)**, including dynamic discovery of tools, **Resources**, and **Prompts**.
2430
- **Smart Agent Capabilities**:
2531
- **Delegation**: `delegate_to_agent` allows spawning specialized sub-agents for complex tasks.
26-
- **RAG & Context**: `codebase_search` allows natural language queries across your codebase. `get_definitions` quickly maps file structures.
27-
- **Planning**: Built-in `scratchpad` memory for long-term planning and note-taking.
28-
- **Visual Safety**: Critical actions like writing files show a **Rich Visual Diff** (Red/Green) in the confirmation modal before execution.
32+
- **RAG & Context**: `codebase_search` allows natural language queries across your codebase.
33+
- **Visual Safety**: Critical actions like writing files show a **Rich Visual Diff** (Red/Green) in the confirmation modal.
2934
- **Advanced Agentic Tools**:
3035
- **Filesystem**: `read_file`, `write_file`, `list_directory`, `edit_file` (precise patching).
31-
- **Git Suite**: `git_status`, `git_diff`, `git_add`, `git_commit`, `git_checkout`, `git_branch`.
32-
- **Enhanced Web Capabilities**: API-backed `web_search` (Tavily, Serper) with DuckDuckGo fallback and clean content extraction via `browse_url`.
33-
- **Code Execution**: `python_sandbox` for isolated logic testing.
34-
- **Human-in-the-Loop (HITL)**: Safety first. Critical actions require explicit user confirmation.
35-
- **Visual Diffs**: Review changes before they happen.
36-
- **Skip/Stop**: Granular control to skip a specific tool or stop the entire process.
36+
- **Dynamic Verbosity**: Level-based control over tool output detail via `/config verbosity`.
37+
- **Git Suite**: Fully featured git integration including diffs and commits.
38+
- **Web Capabilities**: API-backed `web_search` and clean extraction via `browse_url`.
3739
- **Modern TUI**: Built with [Textual](https://textual.textualize.io/), featuring:
3840
- **Collapsible Tool Outputs**: Keep your chat clean while preserving execution details.
39-
- **Dynamic Themes**: `tokyo-night`, `hacker`, `plexir-light`.
4041
- **Live Workspace**: Real-time file tree updates.
4142
- **Command Palette**: `Ctrl+P` for quick actions.
4243
- **Macros & Sessions**: Record complex workflows into macros and persist chat histories across sessions.

ROADMAP.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,25 @@ We have successfully implemented the core foundation and advanced agentic capabi
111111

112112
---
113113

114-
## 🔮 Long Term Vision (v2.0)
114+
## ✅ Completed Milestones (v1.9)
115+
116+
### 🧠 Intelligence & Context
117+
- [x] **Native Token Counting**: Integrated Gemini native token counting API for 100% accurate measurement.
118+
- [x] **Proactive Context Management**: Automatic pruning/summarization when context reaches 90% capacity.
119+
- [x] **Deep Reasoning Support**: Native support for `reasoning_content` (DeepSeek/OpenRouter) and tag normalization.
120+
- [x] **Configurable Reasoning**: Standalone `/config reasoning <on|off>` to control UI transparency of thinking blocks.
121+
- [x] **Session-Scoped Scratchpad**: Isolated planning space that persists within a session but doesn't pollute global history.
122+
123+
### 🖥️ UI & Interaction
124+
- [x] **Interactive Message Queue**: Real-time visualization of pending messages with "queued" styling.
125+
- [x] **Queue "Unroll" Logic**: Interactive click-to-edit behavior that pulls queued messages back into the main input.
126+
- [x] **Dynamic Verbosity**: Level-based control over tool output detail via `/config verbosity`.
127+
128+
### 🛡️ Stability & Core
129+
- [x] **Modal Result Pattern**: Standardized `push_screen_wait` for robust human-in-the-loop flows.
130+
- [x] **Race-Condition Hardening**: Improved widget composition to handle rapid LLM streaming and UI updates.
131+
132+
---
115133

116134
### 1. True "IDE-Like" UI
117135
- [ ] **Multi-Tab Interface**: Support multiple open chat buffers or file editors simultaneously.

docs/COMMANDS.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ Sets a maximum dollar amount for the current session to prevent runaway costs.
4343
- **Example**: `/config budget 0.50`
4444
- **Example**: `/config budget 0` (disables limit)
4545

46+
#### `/config verbosity <0|1|2>`
47+
Sets the application verbosity level for tool outputs.
48+
- **`0`**: Normal (truncated tool outputs, concise logs).
49+
- **`1`**: Verbose (full tool outputs, more detailed logs).
50+
- **`2`**: Debug (maximum detail for troubleshooting).
51+
- **Example**: `/config verbosity 1`
52+
53+
#### `/config reasoning <on|off>`
54+
Toggles whether "Thinking" blocks (reasoning process) are expanded or collapsed by default in the chat.
55+
- **Example**: `/config reasoning off`
56+
4657
#### `/config tool <domain> <key> <value>`
4758
Sets a configuration value for a specific tool suite. This is how you provide tokens for external services.
4859
- **`<domain>`**: The tool domain (e.g., `git`, `github`).
@@ -80,11 +91,13 @@ Saves and loads chat histories.
8091
Lists all saved session files.
8192

8293
#### `/session save [name]`
83-
Saves the current conversation history to a file. If `[name]` is omitted, a timestamp is used.
94+
Saves the current conversation history to a file. If `[name]` is omitted, a timestamp is used.
95+
*Note: This also saves the current `scratchpad` state associated with this session name.*
8496
- **Example**: `/session save my-feature-dev`
8597

8698
#### `/session load <name>`
87-
Clears the current chat and loads a saved session.
99+
Clears the current chat and loads a saved session.
100+
*Note: This also loads the `scratchpad` associated with the session name, ensuring planning continuity.*
88101
- **Example**: `/session load my-feature-dev`
89102

90103
#### `/session delete <name>`

docs/configuration.md

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,21 @@ Instead of plain text, you can use:
3030

3131
Plexir automatically manages the context window to prevent model errors when conversations get too long.
3232

33-
### Automatic Limits
34-
Plexir comes with pre-configured token limits for popular models (e.g., 2M tokens for Gemini 1.5 Pro, 128k for GPT-4o). When the conversation history exceeds this limit, Plexir will:
35-
1. **Preserve** the most recent messages.
36-
2. **Preserve** system instructions.
37-
3. **Summarize/Distill** the older parts of the conversation to save space while retaining context.
33+
### Native Token Counting (Gemini)
34+
For Gemini providers, Plexir uses the **native `countTokens` API** to ensure 100% accurate token measurement. For other providers, it uses a word-based heuristic (1.3 tokens per word) to estimate usage.
35+
36+
### Proactive Pruning
37+
When the current conversation history reaches **90% of the provider's context limit**, Plexir will proactively:
38+
1. **Summarize/Distill** older messages (using the active LLM) to save space while retaining critical facts.
39+
2. **Notify** the user with a system message in the chat.
40+
3. **Accumulate** the summary at the top of the history to ensure continuity.
3841

3942
### Manual Configuration (`context_limit`)
40-
You can override the default limit for any provider. This is useful for:
41-
- Testing how models behave with shorter context.
42-
- Forcing stricter limits on "Preview" models to save costs.
43+
You can override the default limit for any provider. This is useful for testing shorter windows or managing costs on preview models.
44+
- **Example**: `/config set "Gemini Primary" context_limit 50000`
45+
- **Example**: `/config set "Groq Backup" context_limit 8000`
4346

44-
To set a strict 50,000 token limit on a provider:
45-
```bash
46-
/config set "Gemini Primary" context_limit 50000
47-
```
48-
*Set to `0` or `null` to use the model's default.*
47+
*Setting to `0` or `null` will use the model's default limit.*
4948

5049
## Failover & Retries
5150

docs/memory.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,18 @@ You can prompt the agent to remember things directly:
2020
Or ask it to recall:
2121
> "Where did I say my keys were?"
2222
23-
## Rolling Summarization
23+
## Session-Scoped Scratchpad (New in v1.9)
2424

25-
When a conversation history becomes too large (exceeding 40 messages), Plexir automatically triggers **Rolling Summarization**.
25+
For tasks that require multi-step planning and temporary note-taking, Plexir provides a `scratchpad` tool.
26+
27+
Unlike the global Memory Bank, the scratchpad is **session-scoped**. This means:
28+
- **Isolation**: Each session (started with a new run or loaded via `/session load`) has its own unique scratchpad file.
29+
- **Plan Continuity**: Plans made in the scratchpad are saved alongside your session. If you load a session tomorrow, the agent will see exactly where it left off in its plan.
30+
- **No Global Noise**: Content in one session's scratchpad won't confuse the agent in a different project or session.
31+
32+
The agent uses the `scratchpad` tool automatically for complex tasks, but you can also view it manually in `~/.plexir/sessions/<session_id>_scratchpad.md`.
33+
34+
## Rolling Summarization & Proactive Pruning
2635

2736
1. **How it works**: Plexir identifies older, unpinned messages and uses the primary LLM to condense them into a concise "BACKGROUND SUMMARY."
2837
2. **Context Preservation**: This summary is injected at the start of the conversation, allowing the model to remember high-level decisions and context while clearing out detailed token-heavy noise.

docs/tools.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Plexir agents are equipped with a powerful set of tools to interact with the sys
3333
| `save_memory` | Saves a specific fact or piece of information to long-term storage (`chromadb`). | No |
3434
| `search_memory` | Retrieves relevant memories based on a semantic query. | No |
3535
| `codebase_search` | Semantically searches code using natural language keywords. | No |
36-
| `scratchpad` | Reads/Writes/Clears a persistent memory file for planning. | No |
36+
| `scratchpad` | Reads/Writes/Clears a session-scoped memory file for planning and note-taking. | No |
3737

3838
## MCP & Extensibility
3939

plexir/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.8.0"
1+
__version__ = "1.9.0"

plexir/core/commands.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ async def _config(self, args: List[str]) -> str:
235235
return await self._config_reorder(sub_args)
236236
elif subcommand == "debug":
237237
return self._config_debug(sub_args)
238+
elif subcommand == "verbosity":
239+
return self._config_verbosity(sub_args)
240+
elif subcommand == "reasoning":
241+
return self._config_reasoning(sub_args)
238242
elif subcommand == "budget":
239243
return self._config_budget(sub_args)
240244
elif subcommand == "tool":
@@ -265,6 +269,8 @@ def _config_help(self) -> str:
265269
- `/config delete <name>`: Delete a provider.
266270
- `/config reorder <name> <up|down>`: Change failover order.
267271
- `/config debug <on|off>`: Toggle debug mode.
272+
- `/config verbosity <0|1|2>`: Set verbosity level (0=Normal, 1=Verbose Tools).
273+
- `/config reasoning <on|off>`: Toggle expanded reasoning blocks by default.
268274
- `/config budget <value>`: Set session cost limit (e.g. 0.50). 0 for no limit.
269275
"""
270276

@@ -290,6 +296,8 @@ def _config_list(self) -> str:
290296
msg += "\n--- Application Settings ---\n"
291297
msg += f"Theme: `{config_manager.config.theme}`\n"
292298
msg += f"Debug Mode: `{'On' if config_manager.config.debug_mode else 'Off'}`\n"
299+
msg += f"Verbosity: `{config_manager.config.verbosity}`\n"
300+
msg += f"Expanded Reasoning: `{'On' if config_manager.config.expanded_reasoning else 'Off'}`\n"
293301
msg += f"Session Budget: `${config_manager.config.session_budget:.2f}`\n"
294302

295303
# Tool specific configs
@@ -325,6 +333,11 @@ async def _config_set(self, args: List[str]) -> str:
325333
if value not in ("auto", "api_key", "oauth"):
326334
return f"Error: Invalid auth_mode '{value}'. Use: auto, api_key, oauth."
327335
p_config.auth_mode = value
336+
elif key == "context_limit":
337+
try:
338+
p_config.context_limit = int(value)
339+
except ValueError:
340+
return "Error: context_limit must be a number."
328341
else:
329342
return f"Error: Unknown property '{key}'."
330343

@@ -375,14 +388,36 @@ async def _config_reorder(self, args: List[str]) -> str:
375388
return f"Error: {e}"
376389

377390
def _config_debug(self, args: List[str]) -> str:
378-
"""Toggles debug mode on or off."""
391+
"""Toggles debug mode."""
379392
if not args: return "Usage: `/config debug <on|off>`"
380393
state = args[0].lower()
381394
if state in ("on", "off"):
382395
config_manager.update_app_setting("debug_mode", state == "on")
383396
return f"Debug mode set to {state}."
384397
return "Error: State must be 'on' or 'off'."
385398

399+
def _config_verbosity(self, args: List[str]) -> str:
400+
"""Sets the application verbosity level."""
401+
if not args: return "Usage: `/config verbosity <0|1|2>`"
402+
try:
403+
val = int(args[0])
404+
if val not in (0, 1, 2):
405+
return "Error: Verbosity must be 0, 1, or 2."
406+
config_manager.update_app_setting("verbosity", val)
407+
return f"Verbosity level set to {val}."
408+
except ValueError:
409+
return "Error: Verbosity must be a number."
410+
411+
def _config_reasoning(self, args: List[str]) -> str:
412+
"""Toggles expanded reasoning process blocks."""
413+
if not args: return "Usage: `/config reasoning <on|off>`"
414+
state = args[0].lower()
415+
if state in ("on", "off"):
416+
config_manager.update_app_setting("expanded_reasoning", state == "on")
417+
return f"Expanded reasoning set to {state}."
418+
return "Error: State must be 'on' or 'off'."
419+
420+
386421
# --- Session Management ---
387422

388423
async def _session(self, args: List[str]) -> str:
@@ -395,11 +430,19 @@ async def _session(self, args: List[str]) -> str:
395430

396431
try:
397432
if subcommand == "save":
398-
return await self.session_manager.save_session_async(self.app.history, sub_args[0] if sub_args else None)
433+
name = sub_args[0] if sub_args else datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
434+
res = await self.session_manager.save_session_async(self.app.history, name)
435+
# Update router session_id to match saved session
436+
self.app.router.session_id = name
437+
await self.app.router.reload_providers()
438+
return res
399439
elif subcommand == "load":
400440
if not sub_args: return "Usage: `/session load <name>`"
401441
name = sub_args[0]
402442
self.app.history = self.session_manager.load_session(name)
443+
# Update router session_id to match loaded session for scratchpad consistency
444+
self.app.router.session_id = name
445+
await self.app.router.reload_providers()
403446
return f"Session '{name}' loaded. Chat history updated."
404447
elif subcommand == "list":
405448
sessions = self.session_manager.list_sessions()

plexir/core/config_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ class AppConfig(BaseModel):
9191
mcp_servers: Dict[str, MCPServerConfig] = Field(default_factory=dict, description="Configured MCP servers.")
9292
theme: str = "tokyo-night"
9393
debug_mode: bool = False
94+
verbosity: int = 0
95+
expanded_reasoning: bool = False
9496
session_budget: float = 0.0 # 0.0 means no limit
9597
pricing: Dict[str, tuple[float, float]] = Field(
9698
default_factory=lambda: {

0 commit comments

Comments
 (0)