Skip to content

[Feature & Refactor] Support gateway and APPs #13

Merged
wangxingjun778 merged 21 commits into
mainfrom
feat/core_loop
Jul 9, 2026
Merged

[Feature & Refactor] Support gateway and APPs #13
wangxingjun778 merged 21 commits into
mainfrom
feat/core_loop

Conversation

@wangxingjun778

@wangxingjun778 wangxingjun778 commented Jul 9, 2026

Copy link
Copy Markdown
Member

Core Runtime & Architecture

  • Shared Daemon Runtime (leapd): Introduced a background daemon enabling multi-window persistence and communication over Unix sockets.
  • Unified Database Storage: Consolidated six legacy DuckDB instances into a single leap.duckdb file via a shared ConnectionHolder protocol.
  • External Gateway Integration: Added a platform integration layer featuring encrypted credential storage and a unified safety approval gate.

TUI Enhancements

  • Serial Command Queuing: Implemented sequential execution queues for user commands.
  • Contrast-Aware Themes: Added accessible, high-contrast visual themes for improved readability.
  • Session Exit Summaries: Introduced structured terminal reports upon session closure.

Key Bug Fixes & Mitigations

  • Runtime & Type Safety: Patched a missing self.imm attribute initialization and fixed a potential TypeError in session routing.
  • Concurrency & State Control: Resolved race conditions in narrative memory writes and mitigated state corruption risks during concurrent daemon executions.
  • Database Performance: Replaced blocking synchronous sleep calls with non-blocking logic in the write loop and capped infinite retries on permanent DB errors.
  • Security & Reliability: Remedied a path traversal vulnerability in profile path construction and stabilized fragile platform auto-connection logic.

wangxingjun778 and others added 16 commits July 8, 2026 19:18
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>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/leapflow/cli/context.py Outdated
Comment thread src/leapflow/memory/providers/narrative.py
Comment thread src/leapflow/daemon/service.py Outdated
Comment on lines +79 to +80
async def engine_chat(self, message: str, **kwargs: Any) -> AsyncIterator[StreamChunk]:
ctx = self.context

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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

Comment thread src/leapflow/gateway/session_router.py Outdated
Comment on lines +33 to +40
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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Comment thread src/leapflow/gateway/server.py
Comment thread src/leapflow/storage/write_buffer.py
Comment thread src/leapflow/config.py
Comment thread src/leapflow/gateway/server.py
wangxingjun778 and others added 5 commits July 10, 2026 02:21
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>
@wangxingjun778 wangxingjun778 merged commit 28b4d75 into main Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant