This commit bundles a coordinated fix set across frontend and backend runtime flows:\n\n1) Markdown table overflow UX\n- Wrap rendered markdown tables in a dedicated horizontal-scroll container.\n- Preserve wrapper attributes through sanitize so styles always apply.\n- Keep page-level horizontal scroll disabled while allowing in-container table scroll.\n- Improve table container visual consistency (border/radius/shadow/scrollbar behavior).\n\n2) Code block horizontal jitter\n- Adjust code/pre scrollbar gutter and box model handling to remove left-right jump.\n\n3) Execution mode auto-fallback sync + toast behavior\n- Add expiry-driven frontend sync timer for direct->sandbox fallback state refresh.\n- Ensure permission/execution UI state updates at expiry time.\n- Keep fallback warning toast persistent but avoid false-positive toasts when simply switching to sandbox conversations.\n- Trigger fallback toast only when a real direct-expiry transition is detected.\n\n4) Runtime guidance/notify display consistency\n- Unify live polling rendering behavior with history replay behavior.\n- Restore real-time display of guidance/notify/sub-agent/background-command messages.\n- Keep runtime_mode_notice hidden only in idle state as requested.\n\n5) Backend runtime notice emission policy\n- Suppress runtime mode notice insertion when no task is running (idle path).\n- Preserve notice injection when task is actively running.\n\n6) Tool-call ordering safety for injected completion notices\n- Delay inline sub-agent/background-command completion message insertion until all parallel tool calls in the same assistant turn are finished.\n- Prevent invalid assistant/tool_call ordering that causes provider 400 errors for missing tool_call_id responses.\n\n7) Left panel scrolling behavior\n- Enable vertical scrolling for project files / todo / sub-agent / background-command panels.\n- Hide panel scrollbars while preserving scroll capability.\n\n8) Tavily multi-key selection support\n- Introduce config/search.py with selectable env variable name for Tavily key resolution.\n- Keep existing AGENT_TAVILY_API_KEY naming compatible.\n- Export search config through config package.\n\n9) Dedicated conversation-title model config\n- Add AGENT_TITLE_API_BASE_URL / AGENT_TITLE_API_KEY / AGENT_TITLE_MODEL_ID.\n- Route title generation calls to these dedicated credentials/model with fallback defaults.\n\n10) Supporting updates\n- Update .env.example to document new Tavily and title-generation env vars.\n- Include current custom model profile tweak (kimi-k2.6).\n\nValidated with:\n- npm run build\n- python3 -m py_compile (affected backend/config modules)\n- python3 -m unittest test.test_server_refactor_smoke
76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
"""Config package initializer,保持对旧 `from config import ...` 的兼容。"""
|
||
|
||
import os
|
||
from pathlib import Path
|
||
|
||
|
||
def _load_dotenv():
|
||
"""在未显式导入 python-dotenv 的情况下,尽量从仓库根目录加载 .env。"""
|
||
env_path = Path(__file__).resolve().parents[1] / ".env"
|
||
if not env_path.exists():
|
||
return
|
||
# 仅保护启动前已有的环境变量;.env 内重复键以后者为准
|
||
pre_existing_keys = set(os.environ.keys())
|
||
try:
|
||
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
||
line = raw_line.strip()
|
||
if not line or line.startswith("#"):
|
||
continue
|
||
if "=" not in line:
|
||
continue
|
||
key, value = line.split("=", 1)
|
||
key = key.strip()
|
||
value = value.strip().strip('"').strip("'")
|
||
if not key:
|
||
continue
|
||
# 若在进程启动前已存在,则尊重外部环境;否则允许 .env 内后续行覆盖前面行
|
||
if key in pre_existing_keys:
|
||
continue
|
||
os.environ[key] = value
|
||
except Exception:
|
||
# 加载失败时静默继续,保持兼容
|
||
pass
|
||
|
||
|
||
_load_dotenv()
|
||
|
||
from . import api as _api
|
||
from . import search as _search
|
||
from . import paths as _paths
|
||
from . import limits as _limits
|
||
from . import terminal as _terminal
|
||
from . import conversation as _conversation
|
||
from . import security as _security
|
||
from . import ui as _ui
|
||
from . import memory as _memory
|
||
from . import ocr as _ocr
|
||
from . import todo as _todo
|
||
from . import auth as _auth
|
||
from . import uploads as _uploads
|
||
from . import sub_agent as _sub_agent
|
||
from . import custom_tools as _custom_tools
|
||
from . import mcp as _mcp
|
||
|
||
from .api import *
|
||
from .search import *
|
||
from .paths import *
|
||
from .limits import *
|
||
from .terminal import *
|
||
from .conversation import *
|
||
from .security import *
|
||
from .ui import *
|
||
from .memory import *
|
||
from .ocr import *
|
||
from .todo import *
|
||
from .auth import *
|
||
from .uploads import *
|
||
from .sub_agent import *
|
||
from .custom_tools import *
|
||
from .mcp import *
|
||
|
||
__all__ = []
|
||
for module in (_api, _search, _paths, _limits, _terminal, _conversation, _security, _ui, _memory, _ocr, _todo, _auth, _uploads, _sub_agent, _custom_tools, _mcp):
|
||
__all__ += getattr(module, "__all__", [])
|
||
|
||
del _api, _search, _paths, _limits, _terminal, _conversation, _security, _ui, _memory, _ocr, _todo, _auth, _uploads, _sub_agent, _custom_tools, _mcp
|