EvoScientist 0.0.1.dev7__tar.gz → 0.0.1.dev8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/EvoScientist.py +119 -64
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/__init__.py +12 -2
- evoscientist-0.0.1.dev8/EvoScientist/channels/__init__.py +44 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/base.py +1018 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/bus/__init__.py +6 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/bus/events.py +52 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/bus/message_bus.py +96 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/capabilities.py +220 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/channel_manager.py +995 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/config.py +126 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/consumer.py +407 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/discord/__init__.py +19 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/discord/channel.py +255 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/discord/probe.py +33 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/discord/serve.py +93 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/formatter.py +287 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/channels/imessage/__init__.py +9 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/imessage/channel_rpc.py +407 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/imessage/serve.py +87 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/middleware.py +814 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/mixins.py +306 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/plugin.py +226 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/retry.py +122 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/slack/__init__.py +20 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/slack/channel.py +291 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/slack/probe.py +48 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/slack/serve.py +99 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/standalone.py +142 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/telegram/__init__.py +17 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/telegram/channel.py +289 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/telegram/probe.py +32 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/telegram/serve.py +81 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/wechat/__init__.py +69 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/wechat/channel.py +865 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/wechat/crypto.py +187 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/wechat/probe.py +72 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/wechat/serve.py +139 -0
- evoscientist-0.0.1.dev8/EvoScientist/channels/wechat/verify_server.py +175 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/cli/__init__.py +1 -1
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/cli/_app.py +4 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/cli/agent.py +7 -3
- evoscientist-0.0.1.dev8/EvoScientist/cli/channel.py +435 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/cli/commands.py +115 -18
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/cli/interactive.py +128 -123
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/cli/mcp_ui.py +1 -1
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/cli/skills_cmd.py +20 -7
- evoscientist-0.0.1.dev8/EvoScientist/config/__init__.py +38 -0
- {evoscientist-0.0.1.dev7/EvoScientist → evoscientist-0.0.1.dev8/EvoScientist/config}/onboard.py +240 -84
- evoscientist-0.0.1.dev7/EvoScientist/config.py → evoscientist-0.0.1.dev8/EvoScientist/config/settings.py +124 -24
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/mcp/client.py +31 -3
- evoscientist-0.0.1.dev8/EvoScientist/middleware/__init__.py +21 -0
- {evoscientist-0.0.1.dev7/EvoScientist → evoscientist-0.0.1.dev8/EvoScientist/middleware}/memory.py +114 -50
- evoscientist-0.0.1.dev8/EvoScientist/middleware/tool_error_handler.py +70 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/paths.py +25 -4
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/prompts.py +9 -5
- evoscientist-0.0.1.dev8/EvoScientist/skills/agent-swarm-protocol/LICENSE.txt +201 -0
- evoscientist-0.0.1.dev8/EvoScientist/skills/agent-swarm-protocol/SKILL.md +144 -0
- evoscientist-0.0.1.dev8/EvoScientist/skills/agent-swarm-protocol/references/cli-compatibility.md +23 -0
- evoscientist-0.0.1.dev8/EvoScientist/skills/agent-swarm-protocol/references/known-issues.md +210 -0
- evoscientist-0.0.1.dev8/EvoScientist/skills/agent-swarm-protocol/references/task-board.md +63 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/display.py +15 -9
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/emitter.py +1 -1
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/events.py +38 -1
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/formatter.py +1 -5
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/tools/skills_manager.py +5 -5
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/utils.py +13 -6
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist.egg-info/PKG-INFO +41 -15
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist.egg-info/SOURCES.txt +54 -5
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist.egg-info/requires.txt +20 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/PKG-INFO +41 -15
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/README.md +25 -14
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/pyproject.toml +12 -1
- evoscientist-0.0.1.dev8/tests/test_agent_mcp_cache.py +59 -0
- evoscientist-0.0.1.dev8/tests/test_bus_integration.py +278 -0
- evoscientist-0.0.1.dev8/tests/test_channel_comprehensive.py +1545 -0
- evoscientist-0.0.1.dev8/tests/test_channel_manager.py +166 -0
- evoscientist-0.0.1.dev8/tests/test_discord_channel.py +71 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_llm.py +11 -11
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_mcp_client.py +152 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_memory_merge.py +1 -1
- evoscientist-0.0.1.dev8/tests/test_message_bus.py +111 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_onboard.py +229 -152
- evoscientist-0.0.1.dev8/tests/test_paths.py +105 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_skills_manager.py +6 -6
- evoscientist-0.0.1.dev8/tests/test_slack_channel.py +87 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_stream_state.py +36 -169
- evoscientist-0.0.1.dev8/tests/test_telegram_channel.py +68 -0
- evoscientist-0.0.1.dev8/tests/test_tool_error_handler.py +219 -0
- evoscientist-0.0.1.dev8/tests/test_wechat_channel.py +458 -0
- evoscientist-0.0.1.dev7/EvoScientist/channels/__init__.py +0 -9
- evoscientist-0.0.1.dev7/EvoScientist/channels/base.py +0 -110
- evoscientist-0.0.1.dev7/EvoScientist/channels/imessage/channel_rpc.py +0 -427
- evoscientist-0.0.1.dev7/EvoScientist/channels/imessage/serve.py +0 -428
- evoscientist-0.0.1.dev7/EvoScientist/cli/channel.py +0 -289
- evoscientist-0.0.1.dev7/EvoScientist/middleware.py +0 -66
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/__main__.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/backends.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/channels/imessage/probe.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/channels/imessage/rpc_client.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/channels/imessage/targets.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/llm/__init__.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/llm/models.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/mcp/__init__.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/sessions.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/skills/find-skills/SKILL.md +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/skills/skill-creator/SKILL.md +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/skills/skill-creator/references/output-patterns.md +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/skills/skill-creator/references/workflows.md +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/skills/skill-creator/scripts/init_skill.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/skills/skill-creator/scripts/package_skill.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/skills/skill-creator/scripts/quick_validate.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/__init__.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/state.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/tracker.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/stream/utils.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/subagent.yaml +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/tools/__init__.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/tools/search.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/tools/skill_manager.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist/tools/think.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist.egg-info/dependency_links.txt +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist.egg-info/entry_points.txt +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/EvoScientist.egg-info/top_level.txt +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/LICENSE +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/setup.cfg +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_backends.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_cli_run_name.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_config.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_event_loop.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_imports.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_prompts.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_sessions.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_stream_emitter.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_stream_events.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_stream_tracker.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_stream_utils.py +0 -0
- {evoscientist-0.0.1.dev7 → evoscientist-0.0.1.dev8}/tests/test_tools.py +0 -0
|
@@ -13,6 +13,7 @@ Usage:
|
|
|
13
13
|
...
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
import json
|
|
16
17
|
from datetime import datetime
|
|
17
18
|
from pathlib import Path
|
|
18
19
|
|
|
@@ -23,17 +24,12 @@ from .backends import CustomSandboxBackend, MergedReadOnlyBackend
|
|
|
23
24
|
from .config import get_effective_config, apply_config_to_env
|
|
24
25
|
from .llm import get_chat_model
|
|
25
26
|
from .mcp import load_mcp_tools
|
|
26
|
-
from .middleware import
|
|
27
|
+
from .middleware import create_memory_middleware, ToolErrorHandlerMiddleware
|
|
27
28
|
from .prompts import RESEARCHER_INSTRUCTIONS, get_system_prompt
|
|
28
29
|
from .utils import load_subagents
|
|
29
30
|
from .tools import tavily_search, think_tool, skill_manager
|
|
30
|
-
from .
|
|
31
|
-
|
|
32
|
-
default_workspace_dir,
|
|
33
|
-
set_active_workspace,
|
|
34
|
-
MEMORY_DIR as _MEMORY_DIR_PATH,
|
|
35
|
-
USER_SKILLS_DIR as _USER_SKILLS_DIR_PATH,
|
|
36
|
-
)
|
|
31
|
+
from . import paths as _paths_mod
|
|
32
|
+
from .paths import set_active_workspace, set_workspace_root
|
|
37
33
|
|
|
38
34
|
# =============================================================================
|
|
39
35
|
# Configuration
|
|
@@ -43,20 +39,25 @@ from .paths import (
|
|
|
43
39
|
_config = get_effective_config()
|
|
44
40
|
apply_config_to_env(_config)
|
|
45
41
|
|
|
46
|
-
#
|
|
47
|
-
|
|
42
|
+
# NOTE: We intentionally do NOT call set_workspace_root() at module level.
|
|
43
|
+
# The CLI (commands.py) calls set_workspace_root() *before* importing this
|
|
44
|
+
# module. A module-level call here would overwrite the CLI's --workdir
|
|
45
|
+
# value with config.default_workdir, violating the priority chain
|
|
46
|
+
# (CLI args > config file). Instead, config.default_workdir is applied
|
|
47
|
+
# as a fallback inside create_cli_agent() when no explicit workspace_dir
|
|
48
|
+
# is provided.
|
|
48
49
|
|
|
49
50
|
# Research limits (from config)
|
|
50
51
|
MAX_CONCURRENT = _config.max_concurrent
|
|
51
52
|
MAX_ITERATIONS = _config.max_iterations
|
|
52
53
|
|
|
53
|
-
# Workspace settings
|
|
54
|
-
|
|
55
|
-
WORKSPACE_DIR = str(
|
|
54
|
+
# Workspace settings (defer dir creation to CLI; here we just resolve paths)
|
|
55
|
+
# Read from the paths module so values reflect any earlier set_workspace_root().
|
|
56
|
+
WORKSPACE_DIR = str(_paths_mod.WORKSPACE_ROOT)
|
|
56
57
|
set_active_workspace(WORKSPACE_DIR)
|
|
57
|
-
MEMORY_DIR = str(
|
|
58
|
+
MEMORY_DIR = str(_paths_mod.MEMORY_DIR) # Shared across sessions (not per-session)
|
|
58
59
|
SKILLS_DIR = str(Path(__file__).parent / "skills")
|
|
59
|
-
USER_SKILLS_DIR = str(
|
|
60
|
+
USER_SKILLS_DIR = str(_paths_mod.USER_SKILLS_DIR)
|
|
60
61
|
SUBAGENTS_CONFIG = Path(__file__).parent / "subagent.yaml"
|
|
61
62
|
|
|
62
63
|
# =============================================================================
|
|
@@ -78,18 +79,12 @@ chat_model = get_chat_model(
|
|
|
78
79
|
provider=_config.provider,
|
|
79
80
|
)
|
|
80
81
|
|
|
81
|
-
# Initialize workspace backend
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
else:
|
|
89
|
-
_workspace_backend = FilesystemBackend(
|
|
90
|
-
root_dir=WORKSPACE_DIR,
|
|
91
|
-
virtual_mode=True,
|
|
92
|
-
)
|
|
82
|
+
# Initialize workspace backend
|
|
83
|
+
_workspace_backend = CustomSandboxBackend(
|
|
84
|
+
root_dir=WORKSPACE_DIR,
|
|
85
|
+
virtual_mode=True,
|
|
86
|
+
timeout=300,
|
|
87
|
+
)
|
|
93
88
|
|
|
94
89
|
# Skills backend: merge user-installed (./skills/) and system (package) skills
|
|
95
90
|
_skills_backend = MergedReadOnlyBackend(
|
|
@@ -120,6 +115,44 @@ tool_registry = {
|
|
|
120
115
|
# Base tools that every agent variant gets (before MCP)
|
|
121
116
|
BASE_TOOLS = [think_tool, skill_manager]
|
|
122
117
|
|
|
118
|
+
# Cache MCP tools by the effective config signature to avoid reconnecting
|
|
119
|
+
# to MCP servers on every `/new` when config is unchanged.
|
|
120
|
+
_MCP_TOOLS_CACHE_KEY: str | None = None
|
|
121
|
+
_MCP_TOOLS_CACHE_VALUE: dict[str, list] | None = None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _mcp_config_signature() -> str:
|
|
125
|
+
"""Return a stable signature for the effective MCP config."""
|
|
126
|
+
from .mcp.client import load_mcp_config
|
|
127
|
+
|
|
128
|
+
cfg = load_mcp_config()
|
|
129
|
+
if not cfg:
|
|
130
|
+
return ""
|
|
131
|
+
try:
|
|
132
|
+
return json.dumps(cfg, sort_keys=True, ensure_ascii=True)
|
|
133
|
+
except TypeError:
|
|
134
|
+
# Fallback for non-JSON-serializable values (should be rare)
|
|
135
|
+
return repr(cfg)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _load_mcp_tools_cached() -> dict[str, list]:
|
|
139
|
+
"""Load MCP tools with config-aware caching."""
|
|
140
|
+
global _MCP_TOOLS_CACHE_KEY, _MCP_TOOLS_CACHE_VALUE
|
|
141
|
+
|
|
142
|
+
cfg_key = _mcp_config_signature()
|
|
143
|
+
if not cfg_key:
|
|
144
|
+
_MCP_TOOLS_CACHE_KEY = ""
|
|
145
|
+
_MCP_TOOLS_CACHE_VALUE = {}
|
|
146
|
+
return {}
|
|
147
|
+
|
|
148
|
+
if _MCP_TOOLS_CACHE_KEY == cfg_key and _MCP_TOOLS_CACHE_VALUE is not None:
|
|
149
|
+
return {k: list(v) for k, v in _MCP_TOOLS_CACHE_VALUE.items()}
|
|
150
|
+
|
|
151
|
+
loaded = load_mcp_tools()
|
|
152
|
+
_MCP_TOOLS_CACHE_KEY = cfg_key
|
|
153
|
+
_MCP_TOOLS_CACHE_VALUE = {k: list(v) for k, v in loaded.items()}
|
|
154
|
+
return {k: list(v) for k, v in loaded.items()}
|
|
155
|
+
|
|
123
156
|
|
|
124
157
|
def _build_base_kwargs(base_backend, base_middleware):
|
|
125
158
|
"""Build agent kwargs *without* MCP (fast, no subprocess spawning)."""
|
|
@@ -136,16 +169,17 @@ def _build_base_kwargs(base_backend, base_middleware):
|
|
|
136
169
|
subagents=subs,
|
|
137
170
|
middleware=base_middleware,
|
|
138
171
|
system_prompt=SYSTEM_PROMPT,
|
|
172
|
+
skills=["/skills/"],
|
|
139
173
|
)
|
|
140
174
|
|
|
141
175
|
|
|
142
176
|
def load_mcp_and_build_kwargs(base_backend, base_middleware):
|
|
143
|
-
"""
|
|
177
|
+
"""Load MCP tools (cached by config) and build agent kwargs.
|
|
144
178
|
|
|
145
|
-
|
|
146
|
-
|
|
179
|
+
Re-connects to MCP servers only when the effective MCP config changes.
|
|
180
|
+
Falls back to base kwargs if no MCP configured.
|
|
147
181
|
"""
|
|
148
|
-
mcp_by_agent =
|
|
182
|
+
mcp_by_agent = _load_mcp_tools_cached()
|
|
149
183
|
if not mcp_by_agent:
|
|
150
184
|
return _build_base_kwargs(base_backend, base_middleware)
|
|
151
185
|
|
|
@@ -176,6 +210,7 @@ def load_mcp_and_build_kwargs(base_backend, base_middleware):
|
|
|
176
210
|
subagents=subs,
|
|
177
211
|
middleware=base_middleware,
|
|
178
212
|
system_prompt=SYSTEM_PROMPT,
|
|
213
|
+
skills=["/skills/"],
|
|
179
214
|
)
|
|
180
215
|
|
|
181
216
|
|
|
@@ -184,8 +219,8 @@ prompt_refs = {
|
|
|
184
219
|
}
|
|
185
220
|
|
|
186
221
|
base_middleware = [
|
|
222
|
+
ToolErrorHandlerMiddleware(),
|
|
187
223
|
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
188
|
-
create_skills_middleware(backend),
|
|
189
224
|
]
|
|
190
225
|
|
|
191
226
|
# Default agent (no checkpointer) — used by langgraph dev / LangSmith / notebooks.
|
|
@@ -214,46 +249,66 @@ def __getattr__(name: str):
|
|
|
214
249
|
def create_cli_agent(workspace_dir: str | None = None, checkpointer=None):
|
|
215
250
|
"""Create agent with checkpointer for CLI multi-turn support.
|
|
216
251
|
|
|
252
|
+
A fresh backend is constructed on every call using the current
|
|
253
|
+
``paths.WORKSPACE_ROOT`` (or the explicit *workspace_dir*), so
|
|
254
|
+
runtime ``set_workspace_root()`` changes are always respected.
|
|
255
|
+
|
|
217
256
|
Args:
|
|
218
|
-
workspace_dir:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
to ``InMemorySaver`` (non-persistent).
|
|
257
|
+
workspace_dir: Per-session workspace directory. If ``None``,
|
|
258
|
+
defaults to the current ``paths.WORKSPACE_ROOT``.
|
|
259
|
+
checkpointer: Optional LangGraph checkpointer. If ``None``,
|
|
260
|
+
falls back to ``InMemorySaver`` (non-persistent).
|
|
223
261
|
"""
|
|
262
|
+
import os as _os
|
|
263
|
+
from . import paths as _paths
|
|
264
|
+
|
|
224
265
|
if checkpointer is None:
|
|
225
266
|
from langgraph.checkpoint.memory import InMemorySaver # type: ignore[import-untyped]
|
|
226
267
|
checkpointer = InMemorySaver()
|
|
227
268
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
269
|
+
# When no explicit workspace_dir is provided, apply config.default_workdir
|
|
270
|
+
# as a fallback. This covers direct callers (notebooks, iMessage server)
|
|
271
|
+
# that never call set_workspace_root() themselves. CLI callers always
|
|
272
|
+
# pass workspace_dir explicitly, so their --workdir is never overwritten.
|
|
273
|
+
if workspace_dir is None:
|
|
274
|
+
if _config.default_workdir:
|
|
275
|
+
set_workspace_root(
|
|
276
|
+
_os.path.abspath(_os.path.expanduser(_config.default_workdir))
|
|
277
|
+
)
|
|
278
|
+
workspace_dir = str(_paths.WORKSPACE_ROOT)
|
|
279
|
+
|
|
280
|
+
# Read paths dynamically so runtime set_workspace_root() changes are picked up
|
|
281
|
+
_mem_dir = str(_paths.MEMORY_DIR)
|
|
282
|
+
_usr_skills_dir = str(_paths.USER_SKILLS_DIR)
|
|
283
|
+
|
|
284
|
+
# Always construct fresh backends from current paths (avoids stale
|
|
285
|
+
# module-level backend when workspace root changed at runtime).
|
|
286
|
+
set_active_workspace(workspace_dir)
|
|
287
|
+
ws_backend = CustomSandboxBackend(
|
|
288
|
+
root_dir=workspace_dir,
|
|
289
|
+
virtual_mode=True,
|
|
290
|
+
timeout=300,
|
|
291
|
+
)
|
|
292
|
+
sk_backend = MergedReadOnlyBackend(
|
|
293
|
+
primary_dir=_usr_skills_dir,
|
|
294
|
+
secondary_dir=SKILLS_DIR,
|
|
295
|
+
)
|
|
296
|
+
# Memory always uses SHARED directory (not per-session) for cross-session persistence
|
|
297
|
+
mem_backend = FilesystemBackend(
|
|
298
|
+
root_dir=_mem_dir,
|
|
299
|
+
virtual_mode=True,
|
|
300
|
+
)
|
|
301
|
+
be = CompositeBackend(
|
|
302
|
+
default=ws_backend,
|
|
303
|
+
routes={
|
|
304
|
+
"/skills/": sk_backend,
|
|
305
|
+
"/memory/": mem_backend,
|
|
306
|
+
},
|
|
307
|
+
)
|
|
253
308
|
|
|
254
309
|
mw = [
|
|
255
|
-
|
|
256
|
-
|
|
310
|
+
ToolErrorHandlerMiddleware(),
|
|
311
|
+
create_memory_middleware(_mem_dir, extraction_model=chat_model),
|
|
257
312
|
]
|
|
258
313
|
|
|
259
314
|
# Re-load MCP tools from current config (picks up /mcp add changes)
|
|
@@ -28,8 +28,6 @@ _EXPORTS: dict[str, tuple[str, str]] = {
|
|
|
28
28
|
"MODELS": (".llm", "MODELS"),
|
|
29
29
|
"list_models": (".llm", "list_models"),
|
|
30
30
|
"DEFAULT_MODEL": (".llm", "DEFAULT_MODEL"),
|
|
31
|
-
# Middleware
|
|
32
|
-
"create_skills_middleware": (".middleware", "create_skills_middleware"),
|
|
33
31
|
# Prompts
|
|
34
32
|
"get_system_prompt": (".prompts", "get_system_prompt"),
|
|
35
33
|
"RESEARCHER_INSTRUCTIONS": (".prompts", "RESEARCHER_INSTRUCTIONS"),
|
|
@@ -45,6 +43,17 @@ _EXPORTS: dict[str, tuple[str, str]] = {
|
|
|
45
43
|
|
|
46
44
|
|
|
47
45
|
def __getattr__(name: str):
|
|
46
|
+
"""Lazily import and cache package-level attributes.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
name: The attribute name to look up.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The resolved attribute value.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
AttributeError: If the name is not in _EXPORTS.
|
|
56
|
+
"""
|
|
48
57
|
target = _EXPORTS.get(name)
|
|
49
58
|
if target is None:
|
|
50
59
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -58,6 +67,7 @@ def __getattr__(name: str):
|
|
|
58
67
|
|
|
59
68
|
|
|
60
69
|
def __dir__() -> list[str]:
|
|
70
|
+
"""List available public attributes including lazy exports."""
|
|
61
71
|
return sorted(set(globals()) | set(_EXPORTS))
|
|
62
72
|
|
|
63
73
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Communication channels for EvoScientist.
|
|
2
|
+
|
|
3
|
+
This module provides an extensible interface for different messaging channels
|
|
4
|
+
(iMessage, Telegram, Discord, Slack, WeChat) to communicate with the EvoScientist agent.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base import Channel, RawIncoming, IncomingMessage, OutgoingMessage, chunk_text
|
|
8
|
+
from .bus import MessageBus, InboundMessage, OutboundMessage
|
|
9
|
+
from .capabilities import ChannelCapabilities
|
|
10
|
+
from .channel_manager import ChannelManager, register_channel, create_channel, available_channels
|
|
11
|
+
from .consumer import InboundConsumer
|
|
12
|
+
from .formatter import UnifiedFormatter
|
|
13
|
+
from .middleware import TypingManager
|
|
14
|
+
from .plugin import ChannelPlugin, ChannelMeta, ReloadPolicy
|
|
15
|
+
from .standalone import run_standalone
|
|
16
|
+
|
|
17
|
+
# Backward compat: ChannelServer is now Channel itself
|
|
18
|
+
ChannelServer = Channel
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"Channel",
|
|
22
|
+
"ChannelServer",
|
|
23
|
+
"ChannelManager",
|
|
24
|
+
"MessageBus",
|
|
25
|
+
"RawIncoming",
|
|
26
|
+
"IncomingMessage",
|
|
27
|
+
"OutgoingMessage",
|
|
28
|
+
"InboundMessage",
|
|
29
|
+
"OutboundMessage",
|
|
30
|
+
"InboundConsumer",
|
|
31
|
+
"run_standalone",
|
|
32
|
+
"register_channel",
|
|
33
|
+
"create_channel",
|
|
34
|
+
"available_channels",
|
|
35
|
+
# New modules
|
|
36
|
+
"ChannelCapabilities",
|
|
37
|
+
"UnifiedFormatter",
|
|
38
|
+
"TypingManager",
|
|
39
|
+
"chunk_text",
|
|
40
|
+
# Plugin architecture
|
|
41
|
+
"ChannelPlugin",
|
|
42
|
+
"ChannelMeta",
|
|
43
|
+
"ReloadPolicy",
|
|
44
|
+
]
|