Files
manual_slop/docs/guide_architecture.md
2026-02-22 09:38:31 -05:00

87 lines
6.0 KiB
Markdown

# Guide: Architecture
Overview of the package design, state management, and code-path layout.
---
The purpose of this software is to alleviate the pain points of using AI as a local co-pilot by encapsulating the workflow into a resilient, strictly controlled state machine. It manages context generation, API throttling, human-in-the-loop tool execution, and session-long logging.
There are two primary state boundaries used:
* The GUI State (Main Thread, Retained-Mode via Dear PyGui)
* The AI State (Daemon Thread, stateless execution loop)
All synchronization between these boundaries is managed via lock-protected queues and events.
## Code Paths
### Lifetime & Application Boot
The application lifetime is localized within App.run in gui.py.
1. __init__ parses the global config.toml (which sets the active provider, theme, and project paths).
2. It immediately hands off to project_manager.py to deserialize the active <project>.toml which hydrates the session's files, discussion histories, and prompts.
3. Dear PyGui's dpg contexts are bootstrapped with docking_viewport=True, allowing individual GUI panels to exist as native OS windows.
4. The main thread enters a blocking while dpg.is_dearpygui_running() render loop.
5. On shutdown (clean exit), it performs a dual-flush: _flush_to_project() commits the UI state back to the <project>.toml, and _flush_to_config() commits the global state to config.toml. The viewport layout is automatically serialized to dpg_layout.ini.
### Context Shaping & Aggregation
Before making a call to an AI Provider, the current state of the workspace is resolved into a dense Markdown representation.
This occurs inside aggregate.run.
If using the default workflow, aggregate.py hashes through the following process:
1. **Glob Resolution:** Iterates through config["files"]["paths"] and unpacks any wildcards (e.g., src/**/*.rs) against the designated base_dir.
2. **Summarization Pass:** Instead of concatenating raw file bodies (which would quickly overwhelm the ~200k token limit over multiple rounds), the files are passed to summarize.py.
3. **AST Parsing:** summarize.py runs a heuristic pass. For Python files, it uses the standard ast module to read structural nodes (Classes, Methods, Imports, Constants). It outputs a compact Markdown table.
4. **Markdown Generation:** The final <project>_00N.md string is constructed, comprising the truncated AST summaries, the user's current project system prompt, and the active discussion branch.
5. The Markdown file is persisted to disk (./md_gen/ by default) for auditing.
### AI Communication & The Tool Loop
The communication model is unified under ai_client.py, which normalizes the Gemini and Anthropic SDKs into a singular interface send(md_content, user_message, base_dir, file_items).
The loop is defined as follows:
1. **Prompt Injection:** The aggregated Markdown context and system prompt are injected. (Gemini injects this directly into system_instruction at chat instantiation to prevent history bloat; Anthropic chunks this into cache_control: ephemeral blocks).
2. **Execution Loop:** A MAX_TOOL_ROUNDS (default 10) bounded loop begins.
3. The AI provider is polled.
4. If the provider's stop_reason is ool_use:
1. The loop parses the requested tool (either a read-only MCP tool or the destructive PowerShell tool).
2. If PowerShell, it dispatches a blocking event to the Main Thread (see *On Tool Execution & Concurrency*).
3. Once the result is retrieved, the loop executes a **Dynamic Refresh** (_reread_file_items). Any files currently tracked by the project are pulled from the disk fresh.
4. The tool result, appended with the fresh [FILES UPDATED] block, is sent back to the provider.
5. Once the model outputs standard text, the loop terminates and yields the string back to the GUI callback.
### On Tool Execution & Concurrency
When the AI calls a safe MCP tool (like read_file or search_files), the daemon thread immediately executes it via mcp_client.py and returns the result.
However, when the AI requests run_powershell, the operation halts:
1. The Daemon Thread instantiates a ConfirmDialog object containing the payload and calls .wait(). This blocks the thread on a threading.Event().
2. The ConfirmDialog instance is safely placed in a _pending_dialog_lock.
3. The Main Thread, during its next frame cycle, pops the dialog from the lock and renders an OS-level modal window using dpg.window(modal=True).
4. The user can inspect the script, modify it in the text box, or reject it entirely.
5. Upon the user clicking "Approve & Run", the main thread triggers the threading.Event, unblocking the Daemon Thread.
6. The Daemon Thread passes the script to shell_runner.py, captures stdout, stderr, and exit_code, logs it to session_logger.py, and returns it to the LLM.
### On Context History Pruning (Anthropic)
Because the Anthropic API requires sending the entire conversation history on every request, long sessions will inevitably hit the invalid_request_error: prompt is too long.
To solve this, ai_client.py implements an aggressive pruning algorithm:
1. _strip_stale_file_refreshes: It recursively sweeps backward through the history dict and strips out large [FILES UPDATED] data blocks from old turns, preserving only the most recent snapshot.
2. _trim_anthropic_history: If the estimated token count still exceeds _ANTHROPIC_MAX_PROMPT_TOKENS (~180,000), it slices off the oldest user/assistant message pairs from the beginning of the history array.
3. The loop guarantees that at least the System prompt, Tool Definitions, and the final user prompt are preserved.
### Session Persistence
All I/O bound session data is recorded sequentially. session_logger.py hooks into the execution loops and records:
- logs/comms_<ts>.log: A JSON-L structured timeline of every raw payload sent/received.
- logs/toolcalls_<ts>.log: A sequential markdown record detailing every AI tool invocation and its exact stdout result.
- scripts/generated/: Every .ps1 script approved and executed by the shell runner is physically written to disk for version control transparency.