"""Centralised session-state initialisation for the Streamlit app. All keys used throughout the app are declared here with their default values. Calling init_state() at the top of app.py ensures every key exists before any page tries to read it, preventing KeyError on the first load. """ import streamlit as st from backend.managers.chat_manager import ChatManager def init_state(): """Initialise all Streamlit session-state keys with safe defaults. Uses ``if key not in st.session_state`` guards throughout so that existing values are never overwritten on subsequent reruns — only missing keys are set. This means it is safe to call multiple times per session. """ # ── Sidebar state ───────────────────────────────────────────────────────── # last_selected tracks the previously clicked tree node to detect new clicks # and avoid re-running the same file-open logic on every Streamlit rerender. if "last_selected" not in st.session_state: st.session_state.last_selected = None # Absolute path and workspace-relative path of the currently highlighted folder. # Both are set together; both are cleared together when a folder is deselected. if "selected_folder" not in st.session_state: st.session_state.selected_folder = None if "selected_folder_rel" not in st.session_state: st.session_state.selected_folder_rel = None # ── Chat manager ────────────────────────────────────────────────────────── # ChatManager keeps the full conversation history in memory across reruns. # Instantiated once and reused so history is not lost on page rerenders. if "chat_manager" not in st.session_state: st.session_state.chat_manager = ChatManager() # ── Editor state ────────────────────────────────────────────────────────── # Ordered list of absolute file paths currently open as editor tabs. # The list order determines the visual tab order in the UI. if "open_files" not in st.session_state: st.session_state.open_files = [] # Dict mapping absolute file path → current editor content (may differ from # disk if the user has unsaved changes). if "files_content" not in st.session_state: st.session_state.files_content = {} # Absolute path of the file whose tab is currently active in the editor. # Must always be one of the paths in open_files, or None if no file is open. if "active_file" not in st.session_state: st.session_state.active_file = None # Index of the active tab — kept in sync with active_file for st.tabs(). if "active_tab" not in st.session_state: st.session_state.active_tab = 0 if "is_editing" not in st.session_state: st.session_state.is_editing = False if "code_suggestions" not in st.session_state: st.session_state.code_suggestions = [] # Output dict from the last code execution: {stdout, stderr, return_code}. # Initialised as empty string so the editor view can safely check falsyness. if "code_execution_output" not in st.session_state: st.session_state.code_execution_output = "" # ── Chat state ──────────────────────────────────────────────────────────── # Flat list of {"role": ..., "content": ...} dicts rendered as chat bubbles. # System messages are stored here too but skipped during display. if "chat_history" not in st.session_state: st.session_state.chat_history = [] # ── Agent Mode state ────────────────────────────────────────────────────── # Boolean toggle — True while the UI is in Coding Agent mode. if "agent_mode" not in st.session_state: st.session_state.agent_mode = False # The live CodingAgent instance while a task is running. # Set by _start_agent(), cleared by _reset_agent(). if "coding_agent" not in st.session_state: st.session_state.coding_agent = None # Lifecycle state of the agent: "idle" | "waiting_approval" | "done". # Controls which sub-screen render_agent_mode() displays. if "agent_status" not in st.session_state: st.session_state.agent_status = "idle" # Chronological list of completed step records shown in the Agent Log expander. # Each entry: {"thought": str, "tool": str, "arguments": dict, "result": str} if "agent_log" not in st.session_state: st.session_state.agent_log = [] # The action the agent has proposed but that has not yet been approved or # rejected by the user. Stored as the raw dict returned by propose_next_action(). if "agent_pending_action" not in st.session_state: st.session_state.agent_pending_action = None if __name__ == "__main__": init_state()