[Feature and Refactor] Add TUI and Refactor core loop#12
Conversation
Phase A — Foundation Activation (wiring fixes): - True streaming output with StreamEvent type (chunk/final/tool_call) - EventConsumer Protocol + EventBus batch consumer registration - SkillEvolutionPolicy + EMAConfidencePolicy for trust gradient - ObservationDaemon conditional auto-start on initialize - Evolution pipeline breakpoints 1-2 fix (grades→replay, curiosity→tuner) - PipelineObserver for session-end learning pipeline observability Phase B — Universal Agent Execution Baseline: - Concurrent tool execution via ToolConcurrencyPolicy (asyncio.gather) - Prompt caching strategy (PrefixCacheOptimizer + cache_control markers) - 4-stage context compression (Trim→Summarize→Archive→Drop) - Message sanitizer (surrogate pairs, control chars, null bytes) Phase C — Active Learning Capability: - PatternMiner: LLM-Native pattern discovery from EpisodicMemory - Evolution pipeline breakpoints 3-5 fix (insight routing, delta→skill) - Copilot OS notification renderer (macOS/Linux/Windows + fallback) - Skill degradation/forgetting via inactivity decay Phase D — Robustness Enhancement: - Learning effectiveness evaluation framework (LearningMetrics) - Cold start strategy (ColdStartManager, adaptive thresholds) - Implicit feedback detection (inactivity/undo_storm/app_thrashing/retry) - Privacy compliance framework (PrivacyPolicy + PrivacyManager) - Dangerous operation detector (RiskAssessment + batch/path/irreversible) Code Review fixes: - VLM Tier3 attribute name + LLM interface correction - ImplicitFeedbackObserver subscription leak guard - DangerousOperationDetector root path false positive - NoOpHintRenderer for notification_renderer=none All 126 tests passing. 8 new Protocol interfaces, 13 new modules. SOLID-compliant, configuration-driven, zero hardcoding.
P0 — Correctness fixes: - Fix tool concurrency policy name mismatch: align all tool names with registry_bootstrap.py (gp_file_read, gp_shell_run, etc.) - Wire sync_turn to unified tool loop (both stream and non-stream paths) - Add consecutive tool failure protection to unified loop - Enhance shell security: 2-tier pattern system (hardline + approvable), injectable CommandApprovalGate protocol, CWD blocking, output redaction - Fix critical TypeError: remove invalid failure_counter kwarg from _execute_tools_concurrent call P1 — Wire existing infrastructure: - Inject LLM-backed summarize_fn into ContextCompressor (SummarizeStage now uses real LLM summarization with iterative merge) - Inject token_count_fn for accurate token estimation - Initialize DuckDBConversationStore for session persistence - Register memory_search/memory_add tools in TOOL_DEFINITIONS with late-binding handler pattern via set_memory_manager() - Install RedactingFormatter to logging system (secrets auto-redacted) - Enable PrefixCacheOptimizer via engine.set_cache_strategy() - Initialize MCP Manager from ~/.leapflow/mcp_servers.json P2 — Agent quality: - File operation security: sensitive path guards (.env, .ssh, credentials), binary extension block, device path block, output redaction - Interrupt/cancel mechanism: cancel() method, is_cancelled property, cancel check in both unified loop variants - Context overflow recovery: force_compress on CONTEXT_OVERFLOW errors - Stream/non-stream parity: both paths now have cancellation, consecutive failure tracking, context overflow handling, and sync_turn Review fixes: - Fix _count_consecutive_tool_failures: proper JSON parsing instead of substring matching, remove unused current_count parameter - Remove dead _ToolFailureCounter dataclass - Add context overflow force_compress to stream loop error handling Co-authored-by: Cursor <cursoragent@cursor.com>
P0 Critical: - MCP tool registration: discovered tools now registered into TOOL_DEFINITIONS/TOOL_HANDLERS with threat scanning on descriptions - Session persistence: engine creates/appends to DuckDB sessions, wired via set_conversation_store() from CLI context - Approval gate: interactive CLI gate for dangerous shell commands P1 Important: - TurnRecoveryState: one-shot recovery guards preventing infinite force-compress loops and redundant native→text fallbacks - Tool registration unified: session_search tool added to bridge ConversationStore into agent tool surface - Threat scanning integrated into file_read, file_write, memory_add - Memory snapshot freeze: prefetched context frozen per session for prefix cache stability (PrefixCacheOptimizer recognizes _frozen_memory) P2 Quality: - Tool result budget: max_tool_result_chars configurable via env var (replaces all hardcoded :3000 truncations) - Stream mid-interruption: TurnRecoveryState tracks API/tool errors with one-shot guards in streaming path - Session lineage: fork_session() and end_session() on ConversationStore - Subagent executor: DefaultSubagentExecutor with tool restriction, budget control, and isolated message context - Config deep-merge: YAML overlay (config.yaml) with corrupt file backup - StreamEvent protocol: expanded to tool_start, tool_complete, thinking, status, error types with optional metadata field Co-authored-by: Cursor <cursoragent@cursor.com>
Align streaming and non-streaming unified tool loops (text tool fallback, session persistence, error recovery), reset memory snapshot per stream turn, avoid YAML config polluting os.environ in tests, and tighten subagent truncation to use configured budgets. Co-authored-by: Cursor <cursoragent@cursor.com>
Multi-Provider LLM Architecture (key feature): - FailoverChain: ordered provider list with automatic failover on billing/auth/persistent errors, implements LLMProvider protocol - CredentialPool: multi-key rotation with per-key rate-limit cooldown - AuxiliaryClient: cheap model for summarization, risk classification, title generation; falls back to primary when unconfigured - Config-driven via LEAPFLOW_LLM_FALLBACK_PROVIDERS (JSON) and LEAPFLOW_LLM_AUX_MODEL env vars; comma-separated keys for pool P0 — Production agent loop hardening: - Wire SubagentManager + delegate_task tool into agent tool surface - Preflight compression gate: detect and truncate few-but-huge messages (base64 images, large paste buffers) before LLM call - Tool loop guardrails: RepetitionGuard, StagnationGuard, DominationGuard with CompositeGuardrail; configurable thresholds, halt vs warning P1 — Active learning closed loop: - DuckDB EvolutionStore: skill episodes survive restart; write-behind buffer with read-through hydration on startup - Archive stage wired to SemanticMemoryProvider (compression evictions flow to long-term retrievable storage) - Post-turn memory review: background episode recording from tool call patterns for evolution learning - TurnRecoveryState expanded: provider_failover, disable_thinking, length_continuation, multimodal_strip one-shot guards P2 — Quality improvements: - Smart Approval: auxiliary LLM classifies command risk; auto-approves low-risk (< 0.3), interactive prompt for medium/high - File write approval gate: independent from shell; Protocol-based, auto-approves safe extensions and small writes - DuckDB self-repair: check_and_repair + safe_connect with automatic backup of corrupt databases and clean-slate recovery - Token budget dynamic linking: tool result budget proportional to model's context window (context_length / 20) All 126 existing tests pass. New modules verified with targeted tests. Co-authored-by: Cursor <cursoragent@cursor.com>
- Fix try_restore_primary() inverted guard (primary was never restored) - Add full failover/credential rotation to achat_stream() (was missing) - Wire dynamic tool result budget to engine (was computed but unused) - Extract _check_guardrail() helper for stream/non-stream parity - Add guardrails, post-turn review, and try_restore to streaming path - Fix StagnationGuard to detect native role:tool JSON results - Fire archive_fn in ArchiveStage.apply() (was dead wiring) - Fix evolution hydration duplicate prevention with idempotent skip - Pass cooldown_s from settings to credential pool construction - Log observer errors in FailoverChain instead of bare except pass Co-authored-by: Cursor <cursoragent@cursor.com>
…astructure Engine loop robustness (P0): - Length continuation with partial stream recovery on finish_reason=length - Stale-stream watchdog wrapping async iterators with configurable idle timeout - Full TurnRecoveryState activation: all try_* guards wired to ErrorClassifier (compress, multimodal_strip, provider_failover, credential_rotate, disable_thinking) - Chat session resume from DuckDB via engine.load_session() - Incremental evolution persistence in _post_turn_review (write to DuckDB immediately) Message & context hardening (P1): - MessageHealer: tool argument JSON repair (multi-pass) + interrupted tool sequence closer - Multimodal-aware token estimation (~1600 tokens per image part) - Per-tool execution timeout with configurable per-tool overrides - Per-turn usage/cost tracking (TurnUsageTracker) with structured audit logging - Model capability registry with pattern matching and runtime learning from usage - Session export/import for DuckDBConversationStore Provider & cache improvements (P2): - Per-provider circuit breaker in FailoverChain (CLOSED/OPEN/HALF_OPEN states) - Anthropic-optimized cache strategy with multi-breakpoint cache_control placement - Config validation with actionable warning messages New modules: stale_stream.py, turn_usage.py, model_capabilities.py Modified: engine.py (+290/-40), message_healer.py, context_compressor.py, prompt_cache.py, provider_chain.py, conversation_store.py, config.py, context.py All 126 tests pass. Co-authored-by: Cursor <cursoragent@cursor.com>
…world model Close 12 pipeline gaps identified in the advantage analysis report: P0 fixes: - Fix evolution_store.save_episode dict→kwargs parameter mismatch - SkillMerger full-chain sync: propagate merges to SKILL.md + Registry - Hub pull: route through SkillDocStore instead of broken save_from_hub P1 enhancements: - TurnUsageTracker.to_learning_signal for structured episode context - EventBus.shutdown() with consumer flush on cleanup - Evolution generalize() → persist pattern + notify callback - Tool loop episodes → EventBus learning.episode_recorded events - Tool loop outcomes → ExperienceStore for world-model trajectory P2 enhancements: - Engine live signal injection via WM.get_events_since() - Copilot CopilotEventSubscriber → SpeculativePipeline warmup - L1 Markov state persistence via semantic memory (cold start elimination) Co-authored-by: Cursor <cursoragent@cursor.com>
Critical bugs: - _inject_live_signals: use watermark pattern to prevent duplicate signal messages accumulating on every loop iteration - as_chat_messages: strip internal metadata keys (_event_ts, _event_kind, _event_text) from WM output to avoid leaking into LLM API calls - _persist_l1_markov: add upsert_raw to SemanticMemoryProvider (delete + insert) to prevent duplicate key errors on second session SOLID / design improvements: - SkillMerger: add set_doc_store() setter; engine no longer reaches into merger's private _doc_store attribute (encapsulation fix) - _sync_doc_store: load existing SKILL.md and merge fields into it instead of overwriting with stripped-down SkillDocument (preserves rich content: error_handling, examples, procedure_graph) - _post_turn_review: extract _persist_episode, _bridge_to_experience_store, _emit_episode_event helpers (SRP decomposition) - Episode emit threshold made config-queryable (episode_emit_reward_threshold) OCP / generalizability: - live_signal_kinds and copilot_warmup_event_types now config-driven via Settings + env vars (LEAPFLOW_LIVE_SIGNAL_KINDS, LEAPFLOW_COPILOT_WARMUP_EVENT_TYPES) instead of hardcoded frozensets - _install_via_doc_store: add logger.warning for save/register failures Co-authored-by: Cursor <cursoragent@cursor.com>
_manifest_from_cua_tools referenced non-existent PlatformID.DARWIN, PlatformID.WINDOWS, PlatformID.LINUX — the enum only has version- specific variants (DARWIN_15, DARWIN_26, LINUX_GNOME, LINUX_KDE). Add PlatformID.resolve() static method that auto-detects the current platform variant from OS metadata (mac_ver for Darwin major version, XDG_CURRENT_DESKTOP for Linux DE). This eliminates hardcoded platform mapping in the facade and keeps detection logic co-located with the enum definition. Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the entire os_host/ Swift codebase and BridgeClient RPC layer. CuaDriver (Rust, MCP stdio) is now the sole platform backend. Key changes: - TrajectoryRecorder replaces VideoRecorder — structured per-action data (screenshots, AX tree, cursor path) via CuaDriver trajectory recording, with local ffmpeg frame extraction - RecordingProfile + ObservationDaemon.apply_profile() replaces os_host's cross-process RecordingMode for high-fidelity capture - CuaDriverClient handles bundle_id → app_name aliasing for Darwin adapter compatibility - ImitationPipeline integrates trajectory context into VLM analysis - Comprehensive cleanup of all stale os_host/BridgeClient references across code, config, docs, and tests - README updated to reflect CuaDriver architecture All 126 tests pass. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce a complete terminal UI built on rich + prompt_toolkit, replacing the bare input()/print() REPL: - tui_app/theme.py: adaptive light/dark detection (COLORFGBG, env var) - tui_app/console.py: Rich-based output (markdown, syntax, panels, tools) - tui_app/input.py: prompt_toolkit session with history, multiline, command completion, auto-suggest, and mode-aware prompts - tui_app/stream.py: Rich Live streaming renderer with thinking display - tui_app/status.py: bottom toolbar status bar (mode, skills, platform) - Rewrite interactive.py to use the new TUI components - Update chat.py to use streaming renderer with markdown output - Fix CuaDriver shutdown log noise (suppress MCP killpg EPERM warning) All 126 tests pass. Co-authored-by: Cursor <cursoragent@cursor.com>
- StreamRenderer: elapsed timers on active tools, persistent tool completion history in scrollback, response label with total elapsed + tool count - StatusBar: context usage with color-coded pressure (ctx:8.5K/128K 6%), compact token formatting, per-turn elapsed timer - LeapConsole: response_label() with elapsed/tool summary, session_info() for startup display (model, platform, cwd, skills, session id), raw property for StreamRenderer access - Interactive loop: session info after banner, context token tracking in status updates, turn start/end timing - README: comprehensive TUI section with feature table, theme config, architecture overview Co-authored-by: Cursor <cursoragent@cursor.com>
Banner: - Two-column Rich Panel layout: logo/model/session on left, categorized tools and skills on right (mirrors Hermes-agent information density) - Compact fallback for narrow terminals (<80 cols) - display_rich_banner() for REPL, display_welcome() for bare entrypoint Slash commands: - CommandDef registry (registry.py): single source of truth for all REPL commands with categories, aliases, and args hints - /status: model, context usage %, platform, session, mode - /tools: categorized tool table with MCP platform tool count - /usage: token breakdown (input/output/total/context) - /model [name]: show or switch active model - /clear: screen clear with banner re-render - /help: categorized display from registry, replaces hardcoded table Integration: - interactive.py dispatch rewritten: resolve_command() -> canonical match - Both /command and bare command input styles supported - LeapInput completer updated for / prefix awareness - Completion entries auto-generated from command registry TypeScript vs Python has no impact on TUI visual quality — terminal UI is purely ANSI + Unicode. Rich is more mature than most TS alternatives. Co-authored-by: Cursor <cursoragent@cursor.com>
Fixes: - Remove animated ASCII banner from cli.py pre-interactive path — the Rich banner in cmd_interactive is now the sole startup display - Fix UnboundLocalError: move _platform_online() definition above the banner call that references it Banner redesign: - Full-width panel with expand=True — no large whitespace gaps - Warm gold/amber/bronze palette (#FFD700, #FFBF00, #CD7F32, #FFF8DC) inspired by Hermes-agent's visual identity - Welcome line with /help hint below the panel - Narrow terminal (<70 cols) single-column compact fallback Theme overhaul: - Dark theme: gold accent, amber highlights, cream text, bronze borders - Light theme: dark gold counterparts for readability - prompt_toolkit styles: warm toolbar bg (#2a2418), gold prompt char - Status bar: gold/amber mode indicators, green connection dot Co-authored-by: Cursor <cursoragent@cursor.com>
Status bar (Hermes-matched layout): - ⚡ model │ 38K/1.0M │ [████░░░░░░] 25% │ 2m │ ⏲ 6s │ ✓ 2m - Unicode progress bar with color thresholds (green/yellow/red) - Session duration timer + per-turn elapsed + uptime checkmark - Compact token formatting (38K, 1.0M) Banner: - Added "· ModelScope" organization line (mirrors Hermes "· Nous Research") - Smart context length display (128K, 1.0M instead of 1000K) - Session ID shows 16 chars Input behavior: - patch_stdout wraps the main loop — output renders above the prompt while the input line stays pinned at the bottom (Hermes fixed-input UX) - Enter submits (no scroll), Alt+Enter for multiline UX polish: - Horizontal rule separator after each user input (visual rhythm) - Consistent indentation throughout the loop Co-authored-by: Cursor <cursoragent@cursor.com>
Root cause: cua-driver MCP subprocess writes async update notices directly to inherited stderr FD, bypassing patch_stdout and corrupting prompt_toolkit — causing hang after the prompt appears. Fixes: - Disable CUA_DRIVER_RS_UPDATE_CHECK by default in child env (opt-in via env var set to 1/true/yes) - Route MCP subprocess stderr to /dev/null via stdio_client errlog - Render banner inside patch_stdout for consistent terminal state - Explicit Enter key submits input (Alt+Enter for newline) Banner layout: - ModelScope org line directly under tagline - Session ID on next line (Hermes-style) - Model name and cwd below metadata block - Welcome message matches Hermes phrasing Co-authored-by: Cursor <cursoragent@cursor.com>
…output Replace PromptSession.prompt_async() with prompt_toolkit Application for Hermes-style fixed-input architecture. All logic runs on a single asyncio event loop (no threads) with an async worker task for input processing. Architecture: - app.py (NEW): LeapApp controller — Application, HSplit layout (spinner + status bar + TextArea), async process_loop via asyncio.Queue - input.py: Simplified to slash-command completer factory (LeapInput removed) - status.py: FormattedTextControl fragments with class:status-bar* styles - stream.py: Accumulator pattern, no rich.Live — output via patch_stdout - interactive.py: Business logic injected as on_input callback to LeapApp Key benefits over previous PromptSession approach: - Fixed input area pinned at terminal bottom (Hermes-style UX) - Persistent spinner widget with elapsed timer during agent runs - Read-only input while agent is running - Modal-ready layout for future approval/clarification widgets - Pure async — no thread-safety concerns Co-authored-by: Cursor <cursoragent@cursor.com>
…sulation 1. chat.py: update to new StreamRenderer(console) constructor and remove duplicate rendering after finish() (was using old raw Console + theme API) 2. app.py: restore mode-aware prompt — add prompt_mode property so the input prompt reflects learning/paused/executing state (was regression) 3. interactive.py: fix console._console.print() encapsulation violation in _handle_skills — use console.print() public API instead 4. console.py: update stale docstring on raw property Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Code Review
This pull request replaces the legacy Swift-based OSHost with a Python-native platform integration using CuaDriver and an ObservationDaemon, alongside a rich interactive Terminal UI (TUI) built on prompt_toolkit and rich. It also introduces several robust engine enhancements, including a multi-stage context compression pipeline, concurrent tool execution, and a multi-provider LLM failover chain. The review identified critical issues where blocking input() calls inside async functions (in interactive.py and context.py) will freeze the asyncio event loop, as well as logic bugs in engine.py regarding redundant session creation and premature loop termination during tool failure counting.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
- Fix _count_consecutive_tool_failures capping at 1: skip interleaved assistant messages (break -> continue) and stop at current user-turn boundary so stale failures from prior turns are not counted - Fix _ensure_session redundant create_session on every turn: only create when session_id is newly generated, avoiding constraint violations and title overwrite - Fix blocking input() freezing asyncio event loop in async contexts: wrap with run_in_executor in interactive teach prompt, dangerous-command approval gate, and file-write approval gate
CUA Driver-Level Architecture Migration: Replaced the legacy Swift-based
OSHostwith a Python-native platform integration driven by a unifiedCuaDriverandObservationDaemonhybrid architecture.Hybrid TUI Terminal: Implemented a rich interactive Terminal UI (TUI) built on
prompt_toolkitandrich, supporting high-performance text streaming and enhanced layout orchestration.Engine & Pipeline Hardening:
Context Compression Pipeline: Introduced a multi-stage context compression pipeline to optimize token usage.
Concurrent Tool Execution: Enabled concurrent, asynchronous execution scheduling for agent tools.
Multi-Provider LLM Failover Chain: Constructed a resilient multi-provider LLM chain with automated failover capabilities.
Async Event Loop Block Fix: Resolved a critical bug in
interactive.pyandcontext.pywhere blockinginput()calls inside async functions froze theasyncioevent loop.Engine Logic & Session Optimization: Fixed redundant session creation in
engine.pyand addressed a bug causing premature event loop termination during tool failure counting.