Skip to content

feat: add production-grade CLI with isolated session engine#3323

Open
heart-scalpel wants to merge 8 commits into
bytedance:mainfrom
heart-scalpel:feature/add-production-cli
Open

feat: add production-grade CLI with isolated session engine#3323
heart-scalpel wants to merge 8 commits into
bytedance:mainfrom
heart-scalpel:feature/add-production-cli

Conversation

@heart-scalpel
Copy link
Copy Markdown

@heart-scalpel heart-scalpel commented May 30, 2026

Fixes #3292

Why

Original Problem / 问题背景:
The original DeerFlow project lacks a production-grade, standalone CLI interface with proper session management and persistence. Users currently have to implement their own session handling, which often leads to:

  • State contamination across sessions
  • Global lock contention in shared database scenarios
  • Data loss risks without proper checkpointing

My Motivation / 个人初衷:

  1. Environment Constraints / 环境限制: Private network restrictions make setting up complex environments (DeerFlow + Langfuse + etc.) extremely challenging. A lightweight CLI that runs locally solves this pain point immediately.
  2. Architecture Exploration / 架构钻研: As a technical professional, I wanted to quickly evaluate the DeerFlow architecture with real-world usage. This CLI enables rapid deployment of skills and MCPs for framework assessment.
  3. Rapid Porting / 快速移植: Facilitates quick migration of core logic from internal agent projects to DeerFlow for validation.

原问题:
原DeerFlow项目缺乏具备完善会话管理和持久化能力的生产级独立CLI运行时。用户需要自行实现会话处理,经常导致:

  • 跨会话状态污染
  • 共享数据库场景下的全局锁竞争
  • 缺乏检查点机制导致的数据丢失风险

个人初衷:

  1. 环境限制: 私网环境下搭建复杂环境(DeerFlow + Langfuse等)非常困难,轻量CLI可立即解决此痛点
  2. 架构钻研: 作为技术人员,希望通过实际使用快速评估DeerFlow架构,快速部署SKILL和MCP进行框架评估
  3. 快速移植: 便于将内部agent项目的核心逻辑快速迁移到DeerFlow进行验证

What changed

Architecture Design / 架构设计:
Implemented per-session SQLite database isolation architecture - each conversation session gets its own isolated database file. This design approach aims to eliminate global lock contention and state contamination issues.

Note / 说明: The complete solution for global lock contention and state contamination is still under development and testing. This PR provides the infrastructure foundation, with further optimizations planned.

Key Features / 主要功能:

  • Interactive chat interface with real-time streaming responses
  • Complete session lifecycle management (create, switch, delete, archive, restore)
  • Session export to Markdown (deduplicated view + full checkpoint audit)
  • Dynamic model switching and skill enable/disable
  • File upload and management
  • Asynchronous persistence (never blocks main event loop)
  • Docker containerization support

Compatibility / 兼容性:

  • No breaking changes - only added new files (cli/ directory)
  • No core modifications - existing DeerFlow functionality untouched
  • 100% compatible with existing DeerFlow ecosystem

架构设计:
实现了每会话独立SQLite数据库隔离架构——每个会话拥有独立的数据库文件。此设计旨在消除全局锁竞争和状态污染问题。

说明: 全局锁竞争和状态污染的完整解决方案仍在开发和测试中。本PR提供了基础设施基础,后续计划进行进一步优化。

主要功能:

  • 支持实时流式响应的交互式聊天界面
  • 完整的会话生命周期管理(创建、切换、删除、归档、恢复)
  • 会话导出为Markdown(去重视图 + 完整检查点审计)
  • 动态模型切换和技能启用/禁用
  • 文件上传与管理
  • 异步持久化(永不阻塞主事件循环)
  • Docker容器化支持

兼容性:

  • 无破坏性变更 - 仅新增文件(cli/目录)
  • 无核心修改 - 现有DeerFlow功能保持不变
  • 100%兼容现有DeerFlow生态系统

Work in Progress / 待完成事项

  • Complete implementation and testing for global lock contention and state contamination resolution

  • 完成全局锁竞争和状态污染解决方案的实现和测试

Problem

The current engine.py uses a single shared DeerFlowClient instance across all sessions, swapping only the SQLite checkpointer on session switch. This misses three categories of global state that leak across sessions:

Layer Global State File Risk
MCP _mcp_tools_cache, _pool mcp/cache.py, mcp/session_pool.py Tool objects and persistent connections survive session switches
Subagent _background_tasks, _subagent_usage_cache subagents/executor.py, tools/builtins/task_tool.py Background task results from session A are visible to list_background_tasks() in session B
Memory _storage_instance cache, _memory_queue agents/memory/storage.py, agents/memory/queue.py In-memory fact cache and queued conversation contexts span sessions

Switching only the checkpointer while sharing these globals means the claimed "full isolation" property is not actually delivered.

Options considered

# Approach Isolation Complexity Fit
1 Fork a subprocess per session Perfect High (IPC, serialization, lifecycle) Multi-tenant server
2 Reset all globals on session switch Good (best-effort) Medium (audit + maintain reset list) Single-user CLI ← chosen
3 Persistence-only isolation (status quo) Weak Low Already insufficient

Why not fork (option 1): The CLI is single-user and single-session-at-a-time. Fork introduces IPC overhead, Python os.fork() safety issues with existing daemon threads (SessionStore writer, _isolated_subagent_loop), and operational complexity (zombie processes, crash recovery, log aggregation) without proportional benefit.

Why option 2: It delivers meaningful isolation for the actual attack surface (MCP connections, subagent results, memory state) at a fraction of the complexity. The residual risk — that a future backend version adds new global state without a reset API — is acceptable for single-user CLI use and can be addressed long-term by a SessionScope context manager in the backend itself.

What this PR changes in engine.py

1. Per-session DeerFlowClient instances

Before:  self.client = DeerFlowClient(...)              # one for all sessions
After:   self._clients[session_id] = DeerFlowClient(...)  # one per session
  • _get_or_create_client(session_id) — lazy creation with checkpointer binding
  • _activate_session(session_id) — closes old checkpointer, creates/retrieves target client, runs global reset
  • client property — returns the current session's client
  • _destroy_client(session_id) — full teardown on delete/archive

2. Global resource reset on every session switch

_reset_shared_resources() is called from _activate_session() only (user-initiated switches). Read operations (get_session_steps, search_sessions, export_*) use per-session clients directly — no reset triggered.

What gets reset:

MCP:       reset_mcp_tools_cache()          → session pool + tool cache
Subagent:  _background_tasks.clear()        → background task results
           _subagent_usage_cache.clear()    → token usage cache
Memory:    get_memory_storage().reload()    → in-memory fact cache
           reset_memory_queue()             → queued conversation contexts

What is intentionally NOT reset (rationale):

Resource Reason
_isolated_subagent_loop Persistent event loop, expensive to recreate, no session-specific state, atexit-managed
_scheduler_pool ThreadPoolExecutor, expensive, stateless
_SYNC_TOOL_EXECUTOR, _SYNC_MEMORY_UPDATER_EXECUTOR Stateless thread pools, atexit-managed
Middleware dicts (todo, loop_detection) Keyed by (thread_id, run_id), naturally scoped; no public reset API

3. Runtime settings persistence

_runtime_settings dict stores user preferences (model, plan mode, subagent, thinking) and injects them into every newly created client. User choices survive session switches.

4. Read methods no longer switch sessions

get_session_steps, get_all_checkpoint_steps, search_sessions now use the target session's client directly via _get_or_create_client() instead of temporarily switching the global checkpointer. Cleaner, faster, no side effects.

Residual risk

This is a best-effort reset. If a future backend version adds new module-level mutable state without a corresponding public reset function, it will NOT be caught here. The long-term fix is a SessionScope context manager in deerflow that encapsulates all per-session resources and guarantees cleanup — at which point the CLI's _reset_shared_resources() can be replaced with SessionScope.__exit__().

Refs: #3291


问题

当前 engine.py所有会话间复用一个 DeerFlowClient 实例,切换会话时只更换 SQLite checkpointer。这漏掉了三类跨会话泄漏的全局状态:

全局变量 文件 风险
MCP _mcp_tools_cache_pool mcp/cache.pymcp/session_pool.py 工具对象和持久连接跨会话存活
Subagent _background_tasks_subagent_usage_cache subagents/executor.pytools/builtins/task_tool.py 会话 A 的后台任务结果在会话 B 中通过 list_background_tasks() 可见
Memory _storage_instance 缓存、_memory_queue agents/memory/storage.pyagents/memory/queue.py 内存事实缓存和待处理会话上下文跨会话共享

只换 checkpointer 不清这些全局状态,"完全隔离" 的宣称无法真正落地。

方案选择

# 方案 隔离性 复杂度 适用场景
1 每会话 fork 子进程 完美 高(IPC、序列化、进程生命周期) 多租户服务器
2 会话切换时重置所有全局资源 良好(尽力而为) 中(审计 + 维护重置列表) 单用户 CLI ← 采用
3 仅持久化状态隔离(现状) 已不满足需求

为什么不选 fork(方案 1): CLI 是单用户、单会话激活的场景。fork 会引入 IPC 开销、Python os.fork() 与已有 daemon 线程(SessionStore 写线程、_isolated_subagent_loop)的安全问题、以及运维复杂度(僵尸进程、崩溃恢复、日志聚合),收益却不成比例。

为什么选方案 2: 它以远低于 fork 的复杂度,覆盖了实际的风险面(MCP 连接、子代理结果、记忆状态)。残余风险 — 未来 backend 版本新增全局变量但未提供 reset API — 在单用户 CLI 场景下可接受,长期可通过 backend 提供 SessionScope 上下文管理器根治。

改动内容

1. 每会话独立的 DeerFlowClient 实例

改前:  self.client = DeerFlowClient(...)                # 全会话共享
改后:  self._clients[session_id] = DeerFlowClient(...)    # 每会话独立
  • _get_or_create_client(session_id) — 懒加载创建,绑定 checkpointer
  • _activate_session(session_id) — 关闭旧 checkpointer,获取/创建目标 client,执行全局 reset
  • client property — 返回当前会话的 client
  • _destroy_client(session_id) — delete/archive 时完整销毁

2. 会话切换时全局资源重置

_reset_shared_resources() 仅在 _activate_session()(用户主动切换)时调用。读操作(get_session_stepssearch_sessionsexport_*)直接用 per-session client,不触发 reset。

重置覆盖:

MCP:       reset_mcp_tools_cache()          → 关闭持久会话 + 清空工具缓存
Subagent:  _background_tasks.clear()        → 清空后台任务结果
           _subagent_usage_cache.clear()    → 清空 token 用量缓存
Memory:    get_memory_storage().reload()    → 重载内存事实缓存
           reset_memory_queue()             → 排空并重建消息队列

有意不重置的资源及理由:

资源 理由
_isolated_subagent_loop 持久化 event loop,重建开销大,无会话状态,atexit 管理
_scheduler_pool ThreadPoolExecutor,重建开销大,无状态
_SYNC_TOOL_EXECUTOR_SYNC_MEMORY_UPDATER_EXECUTOR 无状态线程池,atexit 管理
Middleware 内部 dict(todo、loop_detection) 已按 (thread_id, run_id) key 过,天然隔离,无公开 reset API

3. 运行时偏好持久化

_runtime_settings dict 存储用户偏好(model、plan mode、subagent、thinking),新建 client 时注入。切换会话后偏好不丢失。

4. 读方法不再切换全局状态

get_session_stepsget_all_checkpoint_stepssearch_sessions 直接用目标会话的 client,不再临时切换全局 checkpointer。更干净、更快、无副作用。

残余风险

这是尽力而为的重置方案。如果未来 backend 版本新增模块级可变状态但未提供对应的 reset 函数,将无法被捕获。长期方案是在 deerflow 中提供 SessionScope 上下文管理器,封装所有 per-session 资源并保证清理 — 届时 CLI 的 _reset_shared_resources() 可替换为 SessionScope.__exit__()

Refs: #3291

Future Work / 后续补充

Testing / 测试:

  • Add unit tests for engine.py (session lifecycle, checkpoint switching)
  • Add unit tests for session_store.py (async persistence, file operations)
  • Add integration tests for cli.py (command parsing, user interaction)

测试:

  • engine.py 添加单元测试(会话生命周期、检查点切换)
  • session_store.py 添加单元测试(异步持久化、文件操作)
  • cli.py 添加集成测试(命令解析、用户交互)

File Structure

cli/tests/
├── conftest.py              # sys.path + pre-mock DeerFlowClient / SqliteSaver
├── test_engine.py           # 69 tests — DeerFlowProductionEngine
├── test_session_store.py    # 14 tests — SessionStore
└── test_cli.py              # 47 tests — CLI command parsing

test_engine.py

Class Tests Coverage
TestSingleton 2 same instance returned; __init__ guards against re-init
TestGetOrCreateClient 5 client creation, reuse, per-session isolation, runtime settings applied on first creation
TestClientProperty 3 returns None when no session, current session's client, orphan session
TestSessionLifecycle 15 create (auto-UUID, custom ID, invalid ID rejection, duplicate), switch, delete, rename, archive (DB file moved), restore (from archive, not found, already active)
TestEnsureCurrentSession 2 falls back to first session, creates default when store empty
TestExtractSteps 6 empty checkpoints, human/ai turn, duplicate detection across checkpoints, tool calls + results, messages without ID, duplicate tool call marking
TestIntrospectionMethods 4 get_session_steps (default + empty), get_all_checkpoint_steps (basic + no checkpoints)
TestChat 5 auto-creates session when none active, streams response chunks, increments metrics (tokens/tool_calls/turns), auto-titles on first turn, preserves custom title
TestRuntimeControls 7 model switch (success, not found, no client), plan mode on/off, subagent on/off, skill enable/disable (no client, success), settings persist across sessions
TestShutdown 2 calls store.shutdown, destroys all clients + checkpointers
TestExport 6 no active session → None, Markdown file creation (session + all checkpoints), tool call JSON formatting, empty checkpoint note
TestSearch 3 keyword in user input, keyword in AI response, no match
TestFileOperations 5 upload (no session, not found, success), list (no session), delete (no session)
TestListing 3 list sessions, list archives (empty + with files)

test_session_store.py

Class Tests Coverage
TestInitAndDiskLoading 4 creates dirs, loads valid session JSON, skips corrupted files, empty dir starts clean
TestSaveAsync 3 queues write for known session, noop for unknown, coalesces multiple rapid saves
TestWriteWorker 2 exits on None sentinel, drains queue on shutdown
TestDeleteSessionFiles 2 removes file + memory state, clears pending write
TestArchiveSessionFiles 2 moves file to archive, handles unflushed pending write
TestThreadSafety 1 5 threads × 10 concurrent saves — no errors

test_cli.py

Class Tests Coverage
TestSafeInput 3 stripped input, UTF-8 decode error recovery, EOF returns empty
TestMultiLineInput 3 reads until !end, EOF handling, decode error recovery
TestMainCommandDispatch 44 every ! command delegates to correct engine method: session CRUD (!new, !switch, !delete session, !rename, !archive, !archives, !restore, !sessions), export (!export, !export_all), search (!search), debugging (!steps, !steps_all), files (!upload, !files, !delete), models/skills (!models, !use, !skills, !enable, !disable), runtime modes (!plan on/off, !subagent on/off), memory (!memory, !clear), help, exit, multi-line mode, normal chat, null session auto-create, exception handling
TestMainNullSession 1 null current_session_id triggers create_session()

Surface area

  • Frontend UI — page / component / setting / interaction under frontend/
    • 前端UIfrontend/ 目录下的页面/组件/设置/交互
  • Backend API — endpoint / SSE event / request-response shape under backend/app
    • 后端APIbackend/app 目录下的接口/SSE事件/请求响应格式
  • Agents / LangGraph — agent node, graph wiring, langgraph.json, or prompt change
    • 代理 / LangGraph — 代理节点、图连接、langgraph.json 或提示词变更
  • Sandboxdocker/ or sandboxed execution
    • 沙箱docker/ 目录或沙箱执行环境
  • Skills — change under skills/
    • 技能skills/ 目录下的变更
  • Dependencies — new/upgraded entry in backend/pyproject.toml or frontend/package.json (say what it buys us)
    • 依赖backend/pyproject.tomlfrontend/package.json 中新增/升级的依赖(请说明带来的好处)
  • Default behavior change — changes existing behavior without the user opting in (default model, default setting, data shape)
    • 默认行为变更 — 在用户未主动选择的情况下改变现有行为(默认模型、默认设置、数据格式)
  • Docs / tests / CI only — no runtime behavior change to existing components
    • 仅文档/测试/CI — 对现有组件无运行时行为变更

Screenshots / Recording

N/A (CLI tool, no UI changes)
不适用(CLI工具,无UI变更)

Bug fix verification

N/A (new feature)
不适用(新功能)

Validation

  • Local end-to-end testing completed, CLI runs correctly

  • Session isolation verified with 5+ concurrent sessions (no cross-session contamination)

  • Session export verified (deduplicated and full checkpoint export)

  • Archive/restore functionality tested

  • Docker image builds successfully and runs in container

  • UTF-8 compatibility tested with Chinese characters

  • Harness package integration verified

  • 本地端到端测试完成,CLI运行正常

  • 会话隔离验证通过5+个并发会话测试(无跨会话污染)

  • 会话导出已验证(去重导出和全检查点导出)

  • 归档/恢复功能已测试

  • Docker镜像构建成功并可在容器中运行

  • UTF-8兼容性使用中文字符测试通过

  • Harness包集成已验证

Quick Start

# Install harness (development mode)
make config

cd backend/packages/harness
pip install -e .

cd ../../../cli
python cli.py

**DeerFlow Production CLI** - Built for production, designed for developers

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 30, 2026

CLA assistant check
All committers have signed the CLA.

@xg-gh-25
Copy link
Copy Markdown

Session isolation in the CLI is critical — without it, parallel invocations clobber each other's state (think CI runners hitting the same agent simultaneously). Sounds like you're architecting for production-grade multi-tenancy from day 1.

Key architectural questions:

  1. Session lifecycle: Are sessions ephemeral (destroyed on CLI exit) or durable (persist across invocations)? Ephemeral = simpler, but you lose conversation history. Durable = need GC policy (when do old sessions expire?).

  2. Concurrency model: Are multiple sessions per agent allowed? If yes, how do you prevent state races when two CLI calls modify the same agent config simultaneously?

# Lock-free session isolation pattern
class SessionEngine:
    def __init__(self, agent_id, session_id):
        # Each session gets private workspace
        self.workspace = f".deer-flow/agents/{agent_id}/sessions/{session_id}"
        self.lockfile = f"{self.workspace}/.lock"
        
    def __enter__(self):
        # Atomic session claim
        try:
            os.mkdir(self.workspace)  # Fails if exists
        except FileExistsError:
            raise SessionConflict("Session already active")
        return self
        
    def __exit__(self, *args):
        # Clean ephemeral state
        if self.config.ephemeral:
            shutil.rmtree(self.workspace)

Performance: Isolated sessions mean each CLI invocation cannot share warm caches (loaded models, indexed embeddings). Are you planning a daemon mode to keep sessions hot between calls? Otherwise deer-flow run = cold start every time (10s+ latency on first message).

Q: What's the session identity scheme? UUIDs (anonymous, no reuse) or user-scoped IDs (let users resume --session=my-debug-session)? The latter needs a registry (~/.deer-flow/sessions.json).


via SwarmAI community engine

@heart-scalpel
Copy link
Copy Markdown
Author

@xg-gh-25
Thanks so much for this incredibly thoughtful review! You nailed the core lock-free isolation pattern perfectly — this is exactly the kind of production-focused feedback we need.

I split the full session management logic into a separate session_store.py layer that addresses most of these points, and went a step further than agent-level locks:

  • Every session gets its own dedicated SQLite database for perfect state isolation (zero global lock contention)
  • Full named session registry with --resume support + archive/restore/rename/search
  • Async background persistence with write coalescing to avoid blocking the main loop

You are absolutely spot-on about the two remaining gaps:

  1. Automatic GC for stale sessions (currently only manual delete/archive)
  2. Daemon mode for hot sessions and shared model caches

Both are top priorities for v0.2. Here's the full session store implementation:
https://github.com/heart-scalpel/deer-flow/blob/0dce27d6cf4f557568fca7dc16c8da3bcd461aba/cli/session_store.py

Really appreciate you taking the time to dig into the code! Let me know if you spot anything else.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a standalone DeerFlow CLI intended to provide isolated per-session execution, persistence, checkpoint inspection/rollback, exports, uploads, and Docker-based local usage.

Changes:

  • Adds a new interactive CLI and session engine built on DeerFlowClient with per-session SQLite checkpoint files.
  • Adds async JSON metadata persistence plus archive/restore support for sessions.
  • Adds Chinese README and Docker/Compose files for CLI setup and usage.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
cli/cli.py Adds interactive command parsing for chat, sessions, rollback, uploads, models, skills, modes, and memory.
cli/engine.py Adds session-aware DeerFlow runtime, checkpoint handling, exports, uploads, and runtime configuration helpers.
cli/session_store.py Adds async metadata persistence and session file archive/delete management.
cli/README_zh.md Documents CLI features, setup, Docker usage, commands, and architecture.
cli/Dockerfile Adds a slim Python image that installs the harness package.
cli/docker-compose.yaml Adds a Compose service for running the CLI container.
cli/__init__.py Adds package marker file.

Comment thread cli/engine.py Outdated
Comment thread cli/engine.py Outdated
Comment thread cli/session_store.py Outdated
Comment thread cli/README_zh.md Outdated
Comment thread cli/docker-compose.yaml
Comment thread cli/cli.py Outdated
Comment thread cli/session_store.py
Comment thread cli/engine.py Outdated
Comment thread cli/engine.py Outdated
Comment thread cli/engine.py
@heart-scalpel heart-scalpel force-pushed the feature/add-production-cli branch from a50e60a to f366b8e Compare May 31, 2026 15:05
@WillemJiang WillemJiang added this to the 2.0.0 milestone May 31, 2026
  - Mark back/back_cp as TODO: DeerFlowClient does not forward
    checkpoint_id to agent runtime yet
  - Fix _write_worker: ensure task_done() is always called via finally,
    preventing shutdown() from blocking forever on write failures
  - Fix archive_session_files: flush pending data directly to archive
    path when async write hasn't landed on disk yet
  - Fix message dedup in get_session_steps and get_all_checkpoint_steps:
    use stable content-based key for messages with id=None instead of
    treating them all as duplicates
…across session switches

create_session now rejects duplicate IDs instead of silently overwriting
metadata while keeping stale checkpoint state.  _switch_checkpointer
swaps the checkpointer on the existing DeerFlowClient (via reset_agent)
instead of constructing a fresh one, so user-set model, plan mode,
subagent, and skill preferences survive session switch/new/archive/restore.
…nd global resource reset

Replace the single shared DeerFlowClient with per-session client instances
so that agent state, runtime settings, and LangGraph graphs never leak
across sessions.  On every user-initiated session switch, proactively
reset all known module-level globals in the deerflow backend:

  MCP layer   — close persistent sessions, clear tool cache, reset pool
  Subagent    — clear background task results and token usage cache
  Memory      — reload storage cache, drain and reset update queue

Runtime preferences (model, plan mode, subagent, thinking) are persisted
in _runtime_settings and applied to every newly created client, so user
choices survive session switches.

Read operations (get_session_steps, search_sessions, export) now use
per-session clients directly without triggering a global reset, keeping
introspection cheap and side-effect-free.

This is a best-effort reset approach — it depends on the backend exposing
public reset APIs for every piece of mutable global state.  For single-user
CLI use this is the right trade-off; a multi-tenant server should spawn a
subprocess per session for guaranteed isolation (see bytedance#3292).
@heart-scalpel heart-scalpel force-pushed the feature/add-production-cli branch from 1a17490 to 945f35e Compare June 1, 2026 16:50
… error handling

Add new CLI commands and engine features for better session monitoring and troubleshooting:

- Add !diagnose command to analyze tool call patterns and detect potential loops
- Add !status command to display current session state and runtime settings
- Add !recursion_limit <N> command to configure recursion limit (default: 1000)
- Add checkpoint count warnings when approaching recursion limit (80% and 90% thresholds)
- Improve error handling with detailed diagnostics and troubleshooting guidance
- Add stream error handling with tool call summary on failure
- Update README_zh.md with new commands and add offline environment setup guide (tiktoken cache)
@heart-scalpel heart-scalpel force-pushed the feature/add-production-cli branch from 3dc9fad to 4855692 Compare June 3, 2026 03:59
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.

[Feature] CLI 引擎,支持会话级完全隔离

5 participants