[Feature & Refactor] Support gateway and APPs #13
Conversation
Implement external platform integration (Feishu, DingTalk, Telegram) via declarative YAML manifests and conversational configuration flow. Gateway module: - PlatformAdapter Protocol + PlatformAdapterMixin for graceful degradation - GatewayServer: adapter lifecycle, message routing, session tracking - ManifestLoader: declarative YAML platform discovery - CredentialVault: Fernet-encrypted at-rest credential storage - SessionKey: structured, immutable session routing - GatewayConfigStore: atomic writes, env-var overrides - Validators: credential pre-flight checks (Feishu, DingTalk, Telegram) - EventBus integration: GatewayMessageReceived, SessionCreated/Ended Security (six-layer defense): - Credentials never appear in LLM context or tool results - Expanded redaction patterns for gateway tokens in logs - File-read blocking for gateway.yaml and .credential_key - Validator URL logging suppression (aiohttp trace_configs) TUI integration: - /gateway slash command: connected/configured/available status panel - /status enhanced with gateway connection info - Startup banner shows gateway-connected platforms - Tab completion and /help include gateway commands Config-as-Conversation UX: - gateway_connect tool with guide/connect/disconnect/remove/status - Manifest-driven setup guides with prompt hints for 1-turn setup - Environment variable overrides for CI/CD deployments README updated with Gateway section, custom adapter guide, and env docs. Co-authored-by: Cursor <cursoragent@cursor.com>
…Flow modules Previously, GatewayServer was constructed but not fully integrated: - on_event callback was not connected (events disappeared) - start() was never called (auto_connect ignored) - set_message_handler was never called (inbound messages dropped) This commit wires the full conduction chain: 1. on_event → episodic memory bridge: inbound gateway messages are logged to EpisodicMemoryProvider for context awareness. Session lifecycle events (created/ended) are logged and trigger router session cleanup. 2. GatewayRouter (new): per-session LLM processing for inbound platform messages. Each external chat gets independent conversation history, isolated from the CLI session. Includes per-session locks to prevent message interleaving and automatic history trimming. 3. GatewayServer.send_reply(): public API for delivering outbound messages through the originating adapter, replacing direct access to _adapters dict. 4. Auto-connect on REPL startup: previously configured platforms reconnect automatically before the welcome banner is rendered. Module boundaries verified: - gateway/ has zero imports from engine/ or cli/ - engine/ has zero imports from gateway/ - context.py is the sole integration point README updated with architecture diagram and refined feature descriptions. Co-authored-by: Cursor <cursoragent@cursor.com>
Bidirectional gateway: - Add gateway_send tool: agent can proactively send messages to any connected platform (Feishu group, Telegram chat, etc.) during reasoning, completing the outbound half of the gateway - Add GatewayServer.send_message(): public API for arbitrary outbound messaging without requiring a prior inbound MessageSource - Add GatewayServer.remove_platform_config(): public API replacing direct _config_store access from tool handlers GatewayRouter tool support: - Router now accepts tool_definitions and tool_handlers, filtered to a SAFE_TOOLS whitelist (memory_search, time_get, skills_list, etc.) - Inbound gateway messages get richer responses: the LLM can search memory, check time, list skills — but cannot run shell or write files - Bounded tool loop (max 3 rounds) prevents runaway tool chains - 30s timeout per tool call within the router Unified tool timeout (engine fix): - ToolBridge dispatch path now wrapped with asyncio.wait_for(), matching the timeout behavior of the TOOL_HANDLERS fallback path - Previously, ToolBridge calls could hang indefinitely while direct handler calls had proper timeout protection Daemon protocol: - Add gateway.send method + METHOD_REGISTRY entry README updated with bidirectional architecture diagram. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace fragmented per-tool approval gates (shell, file write) with a single ApprovalGate Protocol and SessionAwareGate wrapper. Key changes: - ApprovalRequest/ApprovalDecision types in security/approval.py - SessionAwareGate: "always" option skips repeat prompts per category - Rich-styled TUI approval panels (y/n/a) replace raw stderr prompts - gateway_send now requires first-use approval per platform - Consistent fail-closed for non-TTY across all gate types - Audit trail: every decision logged with timestamp and category Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a shared daemon runtime (leapd) for LeapFlow, enabling multi-window persistence over Unix sockets, consolidates six legacy DuckDB databases into a single unified leap.duckdb via a shared ConnectionHolder protocol, and adds an external platform integration (Gateway) with encrypted credentials and a unified safety approval gate. The TUI is also enhanced with serial command queuing, contrast-aware themes, and session exit summaries. The reviewer identified several critical issues, including an uninitialized self.imm attribute causing runtime errors, concurrency and race conditions in narrative memory writes, state corruption risks in concurrent daemon executions, a potential TypeError in session routing, blocking synchronous sleep calls in the database write loop, infinite retries on permanent database errors, a path traversal vulnerability in profile path construction, and fragile platform auto-connection logic.
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.
| async def engine_chat(self, message: str, **kwargs: Any) -> AsyncIterator[StreamChunk]: | ||
| ctx = self.context |
There was a problem hiding this comment.
The AgentEngine is a single shared instance within the daemon. If multiple CLI clients connect and send messages concurrently, they will execute on the same engine instance simultaneously, corrupting the session state, overwriting active session IDs, and interleaving tool executions.
To prevent this, serialize all engine executions using an asyncio.Lock initialized in start().
async def engine_chat(self, message: str, **kwargs: Any) -> AsyncIterator[StreamChunk]:
if self._lock is None:
self._lock = asyncio.Lock()
async with self._lock:
ctx = self.context| def execute_with_retry( | ||
| conn: duckdb.DuckDBPyConnection, | ||
| sql: str, | ||
| params: Any = None, | ||
| *, | ||
| retries: int = _WRITE_RETRIES, | ||
| jitter_ms: Tuple[float, float] = (_JITTER_MIN_MS, _JITTER_MAX_MS), | ||
| ) -> None: |
There was a problem hiding this comment.
Using time.sleep inside synchronous database writes blocks the entire event loop in an async application. Since execute_with_retry is called frequently during runtime (e.g., on every step append or trajectory save), any lock contention will freeze the entire CLI and daemon, preventing other concurrent tasks from running.
Consider running these synchronous database operations in a separate thread pool using asyncio.to_thread or making the retry loop asynchronous.
Three root causes fixed:
1. Engine: usage.get("prompt_tokens", estimate) clobbered the pre-call
estimate when providers return prompt_tokens=0 (common with DashScope/
Qwen). Now only overrides the estimate when provider returns > 0.
2. TUI: in-process _stream_response() never called _update_status() after
turn completion — status bar only refreshed on next user input.
3. Display: _compact_tokens() now uses adaptive precision at all scales
(0.1K, 1.2K, 38K, 256K, 1.2M) for better readability.
Co-authored-by: Cursor <cursoragent@cursor.com>
Core Runtime & Architecture
leapd): Introduced a background daemon enabling multi-window persistence and communication over Unix sockets.leap.duckdbfile via a sharedConnectionHolderprotocol.TUI Enhancements
Key Bug Fixes & Mitigations
self.immattribute initialization and fixed a potentialTypeErrorin session routing.