# 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 .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 .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 _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. For Gemini, the system_instruction and tools are stored in an explicit cache via `client.caches.create()` with a 1-hour TTL; if cache creation fails (under minimum token threshold), it falls back to inline system_instruction. When context changes mid-session, the old cache is deleted and a new one is created. For Anthropic, the system prompt + context are sent as `system=` blocks with `cache_control: ephemeral` on the last chunk, and tools carry `cache_control: ephemeral` on the last tool definition. 2. **Execution Loop:** A MAX_TOOL_ROUNDS (default 10) bounded loop begins. The tools list for Anthropic is built once per session and reused. 3. The AI provider is polled. 4. If the provider's stop_reason is tool_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 last tool result in the batch is retrieved, the loop executes a **Dynamic Refresh** (`_reread_file_items`). Any files currently tracked by the project are pulled from disk fresh. The `file_items` variable is reassigned so subsequent tool rounds see the updated content. 4. For Anthropic: the refreshed file contents are appended as a text block to the tool_results user message. For Gemini: the refreshed contents are appended to the last function response's output string. In both cases, the block is prefixed with `[FILES UPDATED]` / `[SYSTEM: FILES UPDATED]`. 5. On subsequent rounds, stale file-refresh blocks from previous turns are stripped from history to prevent token accumulation. For Gemini, old tool outputs exceeding `_history_trunc_limit` characters are also truncated. 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_.log: A JSON-L structured timeline of every raw payload sent/received. - logs/toolcalls_.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.