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

Commit 9fe762e

Browse files
committed
feat: Release v1.10.0 - Omni-Agent, Security Policy, and Sandbox Siphon
- Omni-Agent: Non-blocking Async Turn Generator and complexity-aware tiered routing. - Security Policy: Hierarchical engine (Allow/Prompt/Forbidden) with JIT approval persistence. - Sandbox Siphon: Optimized context via workspace deltas and process monitoring. - Sub-Agents: Formalized specialized roles (Coder, Tester, Researcher, Reviewer) with tool whitelists. - UX & Interactive: Added /model interactive picker and /yolo autonomous mode toggle. - Infrastructure: New v1.10.0 test suite with 100% pass rate across 51 unit tests. - Docs: Comprehensive update to README, sandbox, and command guides; added new Security Policy documentation.
1 parent a1c7196 commit 9fe762e

33 files changed

Lines changed: 1923 additions & 475 deletions

README.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,27 @@
44

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

7-
[![Version](https://img.shields.io/badge/version-1.9.0-blue.svg)](https://github.com/pomilon/plexir)
7+
[![Version](https://img.shields.io/badge/version-1.10.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

14+
- **Omni-Agent Core (v1.10+)**:
15+
- **Async Turn Generator**: Non-blocking TUI execution for long-running autonomous tasks.
16+
- **State Siphon**: Optimized context via workspace deltas and process monitoring.
1417
- **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.
1518
- **Accuracy & Economics**:
1619
- **Native Token Counting**: Integrated Gemini native token counting API for 100% accurate measurement.
1720
- **Proactive Context Management**: Automatic pruning/summarization when context reaches 90% capacity to prevent truncation errors.
1821
- **Cost Estimation**: Real-time tracking in the sidebar. Set a session budget via `/config budget`.
1922
- **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.
23-
- **Coherent Memory**:
24-
- **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.
26-
- **Rolling Summarization**: Automatically condenses long histories.
27-
- **Message Pinning**: `/session pin` ensures critical context is never lost.
28-
- **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+
- **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. Includes **State Siphon** for tracking file system deltas.
24+
- **Advanced Policy Engine**: Hierarchical security rules for shell commands (Allow/Prompt/Forbidden) with JIT approval persistence.
2925
- **Deep MCP Integration**: Fully supports **Model Context Protocol (MCP)**, including dynamic discovery of tools, **Resources**, and **Prompts**.
3026
- **Smart Agent Capabilities**:
31-
- **Delegation**: `delegate_to_agent` allows spawning specialized sub-agents for complex tasks.
27+
- **Delegation**: `delegate_to_agent` allows spawning specialized sub-agents (`coder`, `tester`, `researcher`, `reviewer`) for complex tasks.
3228
- **RAG & Context**: `codebase_search` allows natural language queries across your codebase.
3329
- **Visual Safety**: Critical actions like writing files show a **Rich Visual Diff** (Red/Green) in the confirmation modal.
3430
- **Advanced Agentic Tools**:

docs/COMMANDS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ This document provides a comprehensive guide to all available slash commands in
99
### `/help`
1010
Displays a summary of all primary slash commands.
1111

12+
### `/model`
13+
Opens an interactive model picker to switch the active provider and model globally. This updates your `config.json` and reloads providers.
14+
15+
### `/yolo`
16+
Toggles YOLO mode (autonomous mode).
17+
- `/yolo start`: Disables safety confirmations for critical tools.
18+
- `/yolo stop`: Enables safety confirmations (default).
19+
1220
### `/clear`
1321
Clears the chat display and current conversation history. This cannot be undone.
1422

docs/policy.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Security Policy Engine
2+
3+
Plexir v1.10 introduces a hierarchical Security Policy Engine to govern the execution of shell commands, especially when running in autonomous (YOLO) mode.
4+
5+
## Overview
6+
7+
The policy engine evaluates every command (and sub-command in chains like `&&` or `;`) against a set of rules. Rules are checked from most specific to least specific.
8+
9+
## Rule Types
10+
11+
1. **ALLOW**: The command is executed immediately.
12+
2. **PROMPT**: The user is asked for confirmation before execution (Default for critical tools).
13+
3. **FORBIDDEN**: The command is blocked entirely, and the AI is informed of the violation.
14+
15+
## Configuration
16+
17+
Rules are stored in `.plexir/rules.txt` within your workspace.
18+
19+
### Format
20+
Each line should follow the format:
21+
`<command_prefix> : <decision> : [justification]`
22+
23+
### Example `rules.txt`
24+
```text
25+
# Allow all git commands
26+
git : allow
27+
28+
# Block dangerous deletions
29+
rm -rf / : forbidden : Destructive root deletion is never allowed.
30+
31+
# Prompt for network access
32+
curl : prompt : Network access requires explicit approval.
33+
pip install : prompt : Package installation should be reviewed.
34+
```
35+
36+
## JIT (Just-In-Time) Approvals
37+
38+
When a command triggers a **PROMPT** decision, Plexir will show a confirmation dialog.
39+
- You can check **"Always allow commands starting with..."** to automatically add a new `ALLOW` rule to your local `.plexir/rules.txt`.
40+
- This allows the engine to learn your preferences as you work.
41+
42+
## Hardcoded Safety Defaults
43+
44+
Plexir includes several built-in safety rules that cannot be overridden by local configurations (unless in YOLO mode):
45+
- `sudo` is **FORBIDDEN**.
46+
- `rm -rf /` is **FORBIDDEN**.
47+
- Network tools like `curl`, `wget` defaults to **PROMPT**.

docs/sandbox.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ When you launch Plexir with the `--sandbox` flag:
1212
## Benefits
1313

1414
- **Safety**: The AI can execute any bash command or Python script without risk to your local files or operating system.
15+
- **State Siphon**: Plexir monitors the sandbox for file changes and active processes, sending optimized deltas to the LLM to keep the context window lean.
1516
- **Persistence**: Unlike one-off sandboxes, the persistent sandbox keeps its state between Plexir sessions. Any files the AI creates or packages it installs (via `apt` or `pip`) will be there the next time you launch with `--sandbox`.
16-
- **Reproducibility**: The sandbox provides a clean, standard environment (`python:3.10-slim`) for the AI to work in.
17+
- **Hardened Security**: The container runs with limited capabilities (`cap_drop=["ALL"]`), preventing unauthorized system-level changes while allowing necessary file operations.
1718

1819
## Technical Details
1920

2021
- **Image**: `python:3.10-slim`
21-
- **Memory Limit**: 512MB
22+
- **Memory Limit**: 1024MB (v1.10+)
2223
- **Network**: Bridge (allows internet access for web tools/package installs).
24+
- **Security Options**: `no-new-privileges` enabled.
2325
- **Graceful Shutdown**: When you exit Plexir, the container is stopped to save resources but is **not** removed, preserving the AI's workspace.
2426

2527
## Troubleshooting

docs/tools.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,20 @@ Plexir agents are equipped with a powerful set of tools to interact with the sys
2929

3030
| Tool | Description | Critical? |
3131
| :--- | :--- | :--- |
32-
| `delegate_to_agent` | Spawns a specialized sub-agent (e.g., `researcher`) to handle complex sub-tasks autonomously. | No |
32+
| `delegate_to_agent` | Spawns a specialized sub-agent to handle complex sub-tasks autonomously. | No |
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 |
3636
| `scratchpad` | Reads/Writes/Clears a session-scoped memory file for planning and note-taking. | No |
3737

38+
### Sub-Agent Roles
39+
When `delegate_to_agent` is used, the agent can specify a role:
40+
- **`coder`**: Optimized for implementation, refactoring, and bug fixes.
41+
- **`tester`**: Specialized in writing and running verification suites.
42+
- **`researcher`**: Uses web tools and documentation to find solutions.
43+
- **`reviewer`**: Analyzes code changes for quality and style.
44+
- **`codebase_investigator`**: Maps large repositories and finds architectural patterns.
45+
3846
## MCP & Extensibility
3947

4048
Plexir dynamically integrates with Model Context Protocol (MCP) servers.

plexir/__init__.py

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

plexir/core/agents.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Specialized Sub-Agent definitions for Plexir.
3+
Defines roles, prompts, and tool whitelists for delegated tasks.
4+
"""
5+
6+
from typing import List, Dict, Optional
7+
8+
class AgentRole:
9+
def __init__(self, name: str, description: str, system_prompt: str, tool_whitelist: Optional[List[str]] = None):
10+
self.name = name
11+
self.description = description
12+
self.system_prompt = system_prompt
13+
self.tool_whitelist = tool_whitelist # None means all tools
14+
15+
SUB_AGENTS = {
16+
"codebase_investigator": AgentRole(
17+
name="codebase_investigator",
18+
description="Specialist in analyzing codebase structure, finding symbols, and mapping dependencies.",
19+
system_prompt=(
20+
"You are a Codebase Investigator. Your goal is to map the project architecture and find specific implementation details.\n"
21+
"Use tools like `get_repo_map`, `codebase_search`, and `get_definitions` extensively.\n"
22+
"Provide a structured report of your findings. DO NOT perform any file edits unless strictly necessary for exploration."
23+
),
24+
tool_whitelist=["get_repo_map", "codebase_search", "get_definitions", "read_file", "list_directory", "grep_search", "scratchpad"]
25+
),
26+
"coder": AgentRole(
27+
name="coder",
28+
description="Specialist in implementing features, fixing bugs, and adding documentation.",
29+
system_prompt=(
30+
"You are a Coder Agent. Your goal is to write high-quality, maintainable code and documentation. "
31+
"Follow the project's coding style and ensure all changes are precise and correct. "
32+
"When adding docstrings, use Google style."
33+
),
34+
tool_whitelist=["write_file", "edit_file", "read_file", "list_directory", "grep_search", "scratchpad"]
35+
),
36+
"tester": AgentRole(
37+
name="tester",
38+
description="Specialist in creating and running tests to verify behavior or reproduce bugs.",
39+
system_prompt=(
40+
"You are a Quality Assurance Agent. Your goal is to ensure code correctness.\n"
41+
"Create test files, run them in the sandbox, and analyze failures.\n"
42+
"If a test fails, explain why and provide a reproduction script."
43+
),
44+
tool_whitelist=["write_file", "run_shell", "python_sandbox", "read_file", "list_directory", "scratchpad"]
45+
),
46+
"researcher": AgentRole(
47+
name="researcher",
48+
description="Specialist in web search and documentation analysis.",
49+
system_prompt=(
50+
"You are a Research Agent. Your goal is to find information from external sources.\n"
51+
"Use `web_search` and `browse_url` to find documentation, latest library versions, or solution patterns."
52+
),
53+
tool_whitelist=["web_search", "browse_url", "scratchpad"]
54+
),
55+
"reviewer": AgentRole(
56+
name="reviewer",
57+
description="Specialist in reviewing code changes for correctness, style, and documentation.",
58+
system_prompt=(
59+
"You are a Reviewer Agent. Your goal is to verify that code changes meet the requirements and maintain high quality. "
60+
"Check for bugs, style issues, and missing documentation. Provide a detailed report of your findings."
61+
),
62+
tool_whitelist=["read_file", "list_directory", "grep_search", "scratchpad"]
63+
)
64+
}
65+
66+
def get_agent_role(name: str) -> Optional[AgentRole]:
67+
return SUB_AGENTS.get(name.lower())

plexir/core/commands.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from typing import List, Optional
1111
from plexir.core.config_manager import config_manager, ProviderConfig
1212
from plexir.core.session import SessionManager
13+
from plexir.ui.screens import ModelPicker
14+
from plexir.ui.widgets import StatsPanel
1315

1416
class CommandProcessor:
1517
"""
@@ -70,6 +72,8 @@ async def process(self, text: str) -> Optional[str]:
7072
return await self._auth(args)
7173
elif cmd == "/yolo":
7274
return self._yolo(args)
75+
elif cmd == "/model":
76+
return await self._model()
7377
elif cmd == "/reload":
7478
await self.app.action_reload_providers()
7579
return "Providers reloaded from config."
@@ -84,6 +88,8 @@ def _help(self) -> str:
8488
return """
8589
**Plexir Commands:**
8690
- `/help`: Show this help message.
91+
- `/model`: Interactively pick provider and model.
92+
- `/yolo [start|stop]`: Toggle autonomous mode.
8793
- `/clear`: Clear the session history.
8894
- `/tools`: List available tools.
8995
- `/config [subcommand]`: Manage application settings.
@@ -192,15 +198,52 @@ def _yolo(self, args: List[str]) -> str:
192198
return f"YOLO Mode is currently **{state}**."
193199
else:
194200
return f"Unknown subcommand: {subcommand}"
195-
196201
def _yolo_help(self) -> str:
197202
return """
198203
**`/yolo` Commands:**
199204
- `/yolo start`: Enable YOLO mode (disable safety checks).
200205
- `/yolo stop`: Disable YOLO mode (enable safety checks).
201-
- `/yolo status`: Check status.
202206
"""
203207

208+
async def _model(self) -> Optional[str]:
209+
"""Handles /model command via an interactive picker."""
210+
if not self.app.router.providers:
211+
return "No providers available. Run /reload."
212+
213+
result = await self.app.push_screen_wait(
214+
ModelPicker(self.app.router.providers, self.app.router.active_provider_index)
215+
)
216+
217+
if result and result != "cancel":
218+
try:
219+
p_idx_str, model_name = result.split(":", 1)
220+
p_idx = int(p_idx_str)
221+
222+
provider = self.app.router.providers[p_idx]
223+
224+
# 1. Update global configuration
225+
p_config = config_manager.get_provider_config(provider.name)
226+
if p_config:
227+
p_config.model_name = model_name
228+
config_manager.update_provider(provider.name, p_config)
229+
230+
# 2. Update failover order (move selected to top)
231+
order = config_manager.config.active_provider_order
232+
if provider.name in order:
233+
order.remove(provider.name)
234+
order.insert(0, provider.name)
235+
config_manager.update_app_setting("active_provider_order", order)
236+
237+
# 3. Reload providers to apply changes globally
238+
await self.app.action_reload_providers()
239+
240+
self.app.notify(f"Globally switched to {provider.name} -> {model_name}")
241+
return f"Switched model to **{provider.name} / {model_name}** globally."
242+
except Exception as e:
243+
return f"Error updating model: {e}"
244+
245+
return None
246+
204247
def _tools(self) -> str:
205248
"""Lists all registered tools."""
206249
tools = self.app.router.registry.list_tools()

0 commit comments

Comments
 (0)