EvoScientist 0.0.1.dev4__tar.gz → 0.0.1.dev6__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.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/EvoScientist.py +107 -35
- evoscientist-0.0.1.dev6/EvoScientist/__init__.py +64 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/backends.py +39 -101
- evoscientist-0.0.1.dev6/EvoScientist/channels/__init__.py +9 -0
- evoscientist-0.0.1.dev6/EvoScientist/channels/base.py +110 -0
- evoscientist-0.0.1.dev6/EvoScientist/channels/imessage/__init__.py +33 -0
- evoscientist-0.0.1.dev6/EvoScientist/channels/imessage/channel_rpc.py +427 -0
- evoscientist-0.0.1.dev6/EvoScientist/channels/imessage/probe.py +106 -0
- evoscientist-0.0.1.dev6/EvoScientist/channels/imessage/rpc_client.py +235 -0
- evoscientist-0.0.1.dev6/EvoScientist/channels/imessage/serve.py +428 -0
- evoscientist-0.0.1.dev6/EvoScientist/channels/imessage/targets.py +232 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/__init__.py +26 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/_app.py +47 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/agent.py +60 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/channel.py +289 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/commands.py +470 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/interactive.py +708 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/mcp_ui.py +282 -0
- evoscientist-0.0.1.dev6/EvoScientist/cli/skills_cmd.py +73 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/config.py +12 -1
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/llm/models.py +20 -4
- evoscientist-0.0.1.dev6/EvoScientist/mcp/__init__.py +32 -0
- evoscientist-0.0.1.dev6/EvoScientist/mcp/client.py +667 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/memory.py +24 -46
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/middleware.py +7 -21
- evoscientist-0.0.1.dev6/EvoScientist/onboard.py +1331 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/paths.py +16 -0
- evoscientist-0.0.1.dev6/EvoScientist/sessions.py +332 -0
- evoscientist-0.0.1.dev6/EvoScientist/skills/find-skills/SKILL.md +76 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/skills/skill-creator/SKILL.md +64 -58
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/__init__.py +2 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/display.py +217 -8
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/events.py +59 -4
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/formatter.py +4 -1
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/utils.py +11 -2
- evoscientist-0.0.1.dev6/EvoScientist/tools/__init__.py +16 -0
- evoscientist-0.0.1.dev6/EvoScientist/tools/search.py +115 -0
- evoscientist-0.0.1.dev6/EvoScientist/tools/skill_manager.py +120 -0
- {evoscientist-0.0.1.dev4/EvoScientist → evoscientist-0.0.1.dev6/EvoScientist/tools}/skills_manager.py +105 -26
- evoscientist-0.0.1.dev6/EvoScientist/tools/think.py +32 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/utils.py +3 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist.egg-info/PKG-INFO +176 -18
- evoscientist-0.0.1.dev6/EvoScientist.egg-info/SOURCES.txt +79 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist.egg-info/requires.txt +7 -3
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/PKG-INFO +176 -18
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/README.md +168 -14
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/pyproject.toml +8 -4
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_backends.py +88 -0
- evoscientist-0.0.1.dev6/tests/test_cli_run_name.py +38 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_config.py +19 -0
- evoscientist-0.0.1.dev6/tests/test_event_loop.py +200 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_imports.py +0 -15
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_llm.py +20 -4
- evoscientist-0.0.1.dev6/tests/test_mcp_client.py +539 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_onboard.py +309 -14
- evoscientist-0.0.1.dev6/tests/test_sessions.py +234 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_skills_manager.py +7 -7
- evoscientist-0.0.1.dev6/tests/test_stream_events.py +87 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_stream_state.py +161 -11
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_stream_utils.py +28 -4
- evoscientist-0.0.1.dev4/EvoScientist/__init__.py +0 -43
- evoscientist-0.0.1.dev4/EvoScientist/cli.py +0 -668
- evoscientist-0.0.1.dev4/EvoScientist/onboard.py +0 -725
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/SKILL.md +0 -332
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/references/custom-plugins.md +0 -453
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/references/megatron-integration.md +0 -489
- evoscientist-0.0.1.dev4/EvoScientist/skills/accelerate/references/performance.md +0 -525
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/SKILL.md +0 -411
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/references/memory-optimization.md +0 -521
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/references/qlora-training.md +0 -521
- evoscientist-0.0.1.dev4/EvoScientist/skills/bitsandbytes/references/quantization-formats.md +0 -447
- evoscientist-0.0.1.dev4/EvoScientist/skills/find-skills/SKILL.md +0 -133
- evoscientist-0.0.1.dev4/EvoScientist/skills/find-skills/scripts/install_skill.py +0 -211
- evoscientist-0.0.1.dev4/EvoScientist/skills/flash-attention/SKILL.md +0 -367
- evoscientist-0.0.1.dev4/EvoScientist/skills/flash-attention/references/benchmarks.md +0 -215
- evoscientist-0.0.1.dev4/EvoScientist/skills/flash-attention/references/transformers-integration.md +0 -293
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/SKILL.md +0 -258
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/references/optimization.md +0 -89
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/references/quantization.md +0 -213
- evoscientist-0.0.1.dev4/EvoScientist/skills/llama-cpp/references/server.md +0 -125
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/SKILL.md +0 -490
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/api-evaluation.md +0 -490
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/benchmark-guide.md +0 -488
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/custom-tasks.md +0 -602
- evoscientist-0.0.1.dev4/EvoScientist/skills/lm-evaluation-harness/references/distributed-eval.md +0 -519
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/SKILL.md +0 -937
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/checklists.md +0 -361
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/citation-workflow.md +0 -562
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/reviewer-guidelines.md +0 -367
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/sources.md +0 -159
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/references/writing-guide.md +0 -476
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/README.md +0 -251
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/README.md +0 -534
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +0 -144
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +0 -952
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bib +0 -111
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bst +0 -1493
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.sty +0 -315
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/README.md +0 -50
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl.sty +0 -312
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl_latex.tex +0 -377
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl_lualatex.tex +0 -101
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/acl_natbib.bst +0 -1940
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/anthology.bib.txt +0 -26
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/custom.bib +0 -70
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/acl/formatting.md +0 -326
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/README.md +0 -3
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bib +0 -11
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bst +0 -1440
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.sty +0 -218
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.tex +0 -305
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/fancyhdr.sty +0 -485
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/math_commands.tex +0 -508
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/colm2025/natbib.sty +0 -1246
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/fancyhdr.sty +0 -485
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bib +0 -24
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bst +0 -1440
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.sty +0 -246
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.tex +0 -414
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/math_commands.tex +0 -508
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/iclr2026/natbib.sty +0 -1246
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithm.sty +0 -79
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithmic.sty +0 -201
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.bib +0 -75
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.tex +0 -662
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/fancyhdr.sty +0 -864
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.bst +0 -1443
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.sty +0 -767
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/Makefile +0 -36
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/extra_pkgs.tex +0 -53
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/main.tex +0 -38
- evoscientist-0.0.1.dev4/EvoScientist/skills/ml-paper-writing/templates/neurips2025/neurips.sty +0 -382
- evoscientist-0.0.1.dev4/EvoScientist/skills/peft/SKILL.md +0 -431
- evoscientist-0.0.1.dev4/EvoScientist/skills/peft/references/advanced-usage.md +0 -514
- evoscientist-0.0.1.dev4/EvoScientist/skills/peft/references/troubleshooting.md +0 -480
- evoscientist-0.0.1.dev4/EvoScientist/skills/ray-data/SKILL.md +0 -326
- evoscientist-0.0.1.dev4/EvoScientist/skills/ray-data/references/integration.md +0 -82
- evoscientist-0.0.1.dev4/EvoScientist/skills/ray-data/references/transformations.md +0 -83
- evoscientist-0.0.1.dev4/EvoScientist/skills/skill-creator/LICENSE.txt +0 -202
- evoscientist-0.0.1.dev4/EvoScientist/tools.py +0 -208
- evoscientist-0.0.1.dev4/EvoScientist.egg-info/SOURCES.txt +0 -132
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/__main__.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/llm/__init__.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/prompts.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/skills/skill-creator/references/output-patterns.md +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/skills/skill-creator/references/workflows.md +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/skills/skill-creator/scripts/init_skill.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/skills/skill-creator/scripts/package_skill.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/skills/skill-creator/scripts/quick_validate.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/emitter.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/state.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/stream/tracker.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist/subagent.yaml +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist.egg-info/dependency_links.txt +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist.egg-info/entry_points.txt +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/EvoScientist.egg-info/top_level.txt +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/LICENSE +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/setup.cfg +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_prompts.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_stream_emitter.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_stream_tracker.py +0 -0
- {evoscientist-0.0.1.dev4 → evoscientist-0.0.1.dev6}/tests/test_tools.py +0 -0
|
@@ -22,6 +22,7 @@ from deepagents.backends import FilesystemBackend, CompositeBackend
|
|
|
22
22
|
from .backends import CustomSandboxBackend, MergedReadOnlyBackend
|
|
23
23
|
from .config import get_effective_config, apply_config_to_env
|
|
24
24
|
from .llm import get_chat_model
|
|
25
|
+
from .mcp import load_mcp_tools
|
|
25
26
|
from .middleware import create_skills_middleware, create_memory_middleware
|
|
26
27
|
from .prompts import RESEARCHER_INSTRUCTIONS, get_system_prompt
|
|
27
28
|
from .utils import load_subagents
|
|
@@ -29,6 +30,7 @@ from .tools import tavily_search, think_tool, skill_manager
|
|
|
29
30
|
from .paths import (
|
|
30
31
|
ensure_dirs,
|
|
31
32
|
default_workspace_dir,
|
|
33
|
+
set_active_workspace,
|
|
32
34
|
MEMORY_DIR as _MEMORY_DIR_PATH,
|
|
33
35
|
USER_SKILLS_DIR as _USER_SKILLS_DIR_PATH,
|
|
34
36
|
)
|
|
@@ -51,6 +53,7 @@ MAX_ITERATIONS = _config.max_iterations
|
|
|
51
53
|
# Workspace settings
|
|
52
54
|
ensure_dirs()
|
|
53
55
|
WORKSPACE_DIR = str(default_workspace_dir())
|
|
56
|
+
set_active_workspace(WORKSPACE_DIR)
|
|
54
57
|
MEMORY_DIR = str(_MEMORY_DIR_PATH) # Shared across sessions (not per-session)
|
|
55
58
|
SKILLS_DIR = str(Path(__file__).parent / "skills")
|
|
56
59
|
USER_SKILLS_DIR = str(_USER_SKILLS_DIR_PATH)
|
|
@@ -73,7 +76,6 @@ SYSTEM_PROMPT = get_system_prompt(
|
|
|
73
76
|
chat_model = get_chat_model(
|
|
74
77
|
model=_config.model,
|
|
75
78
|
provider=_config.provider,
|
|
76
|
-
# thinking={"type": "enabled", "budget_tokens": 2000},
|
|
77
79
|
)
|
|
78
80
|
|
|
79
81
|
# Initialize workspace backend based on mode
|
|
@@ -115,45 +117,116 @@ tool_registry = {
|
|
|
115
117
|
"tavily_search": tavily_search,
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
# Base tools that every agent variant gets (before MCP)
|
|
121
|
+
BASE_TOOLS = [think_tool, skill_manager]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _build_base_kwargs(base_backend, base_middleware):
|
|
125
|
+
"""Build agent kwargs *without* MCP (fast, no subprocess spawning)."""
|
|
126
|
+
subs = load_subagents(
|
|
127
|
+
SUBAGENTS_CONFIG,
|
|
128
|
+
tool_registry=tool_registry,
|
|
129
|
+
prompt_refs=prompt_refs,
|
|
130
|
+
)
|
|
131
|
+
return dict(
|
|
132
|
+
name="EvoScientist",
|
|
133
|
+
model=chat_model,
|
|
134
|
+
tools=list(BASE_TOOLS),
|
|
135
|
+
backend=base_backend,
|
|
136
|
+
subagents=subs,
|
|
137
|
+
middleware=base_middleware,
|
|
138
|
+
system_prompt=SYSTEM_PROMPT,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def load_mcp_and_build_kwargs(base_backend, base_middleware):
|
|
143
|
+
"""(Re-)load MCP tools and build agent kwargs.
|
|
144
|
+
|
|
145
|
+
Called on every ``create_cli_agent()`` call so that ``/new`` picks up
|
|
146
|
+
MCP config changes. Falls back to base kwargs if no MCP configured.
|
|
147
|
+
"""
|
|
148
|
+
mcp_by_agent = load_mcp_tools()
|
|
149
|
+
if not mcp_by_agent:
|
|
150
|
+
return _build_base_kwargs(base_backend, base_middleware)
|
|
151
|
+
|
|
152
|
+
# Fresh tool registry — start from base tools + MCP tools
|
|
153
|
+
registry = dict(tool_registry)
|
|
154
|
+
for tools in mcp_by_agent.values():
|
|
155
|
+
for t in tools:
|
|
156
|
+
registry[t.name] = t
|
|
157
|
+
|
|
158
|
+
mcp_main = mcp_by_agent.pop("main", [])
|
|
159
|
+
|
|
160
|
+
subs = load_subagents(
|
|
161
|
+
SUBAGENTS_CONFIG,
|
|
162
|
+
tool_registry=registry,
|
|
163
|
+
prompt_refs=prompt_refs,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Inject MCP tools into subagents by name
|
|
167
|
+
for sa in subs:
|
|
168
|
+
if sa_tools := mcp_by_agent.get(sa["name"], []):
|
|
169
|
+
sa.setdefault("tools", []).extend(sa_tools)
|
|
170
|
+
|
|
171
|
+
return dict(
|
|
172
|
+
name="EvoScientist",
|
|
173
|
+
model=chat_model,
|
|
174
|
+
tools=BASE_TOOLS + mcp_main,
|
|
175
|
+
backend=base_backend,
|
|
176
|
+
subagents=subs,
|
|
177
|
+
middleware=base_middleware,
|
|
178
|
+
system_prompt=SYSTEM_PROMPT,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
118
182
|
prompt_refs = {
|
|
119
183
|
"RESEARCHER_INSTRUCTIONS": RESEARCHER_INSTRUCTIONS.format(date=current_date),
|
|
120
184
|
}
|
|
121
185
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
186
|
+
base_middleware = [
|
|
187
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
188
|
+
create_skills_middleware(backend),
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
# Default agent (no checkpointer) — used by langgraph dev / LangSmith / notebooks.
|
|
192
|
+
# Lazily constructed on first access so MCP tools are included without
|
|
193
|
+
# spawning subprocesses at import time.
|
|
194
|
+
_EvoScientist_agent = None
|
|
127
195
|
|
|
128
|
-
# Shared kwargs for agent creation
|
|
129
|
-
_AGENT_KWARGS = dict(
|
|
130
|
-
name="EvoScientist",
|
|
131
|
-
model=chat_model,
|
|
132
|
-
tools=[think_tool, skill_manager],
|
|
133
|
-
backend=backend,
|
|
134
|
-
subagents=subagents,
|
|
135
|
-
middleware=[
|
|
136
|
-
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
137
|
-
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
138
|
-
],
|
|
139
|
-
system_prompt=SYSTEM_PROMPT,
|
|
140
|
-
)
|
|
141
196
|
|
|
142
|
-
|
|
143
|
-
|
|
197
|
+
def _get_default_agent():
|
|
198
|
+
"""Build the default agent (with MCP, no checkpointer) on first access."""
|
|
199
|
+
global _EvoScientist_agent
|
|
200
|
+
if _EvoScientist_agent is None:
|
|
201
|
+
kwargs = load_mcp_and_build_kwargs(backend, base_middleware)
|
|
202
|
+
_EvoScientist_agent = create_deep_agent(**kwargs).with_config(
|
|
203
|
+
{"recursion_limit": 500}
|
|
204
|
+
)
|
|
205
|
+
return _EvoScientist_agent
|
|
206
|
+
|
|
144
207
|
|
|
208
|
+
def __getattr__(name: str):
|
|
209
|
+
if name == "EvoScientist_agent":
|
|
210
|
+
return _get_default_agent()
|
|
211
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
145
212
|
|
|
146
|
-
|
|
147
|
-
|
|
213
|
+
|
|
214
|
+
def create_cli_agent(workspace_dir: str | None = None, checkpointer=None):
|
|
215
|
+
"""Create agent with checkpointer for CLI multi-turn support.
|
|
148
216
|
|
|
149
217
|
Args:
|
|
150
218
|
workspace_dir: Optional per-session workspace directory. If provided,
|
|
151
219
|
creates a fresh backend rooted at this path. If None, uses the
|
|
152
220
|
module-level default backend (./workspace).
|
|
221
|
+
checkpointer: Optional LangGraph checkpointer. If None, falls back
|
|
222
|
+
to ``InMemorySaver`` (non-persistent).
|
|
153
223
|
"""
|
|
154
|
-
|
|
224
|
+
if checkpointer is None:
|
|
225
|
+
from langgraph.checkpoint.memory import InMemorySaver # type: ignore[import-untyped]
|
|
226
|
+
checkpointer = InMemorySaver()
|
|
155
227
|
|
|
156
228
|
if workspace_dir:
|
|
229
|
+
set_active_workspace(workspace_dir)
|
|
157
230
|
ws_backend = CustomSandboxBackend(
|
|
158
231
|
root_dir=workspace_dir,
|
|
159
232
|
virtual_mode=True,
|
|
@@ -175,19 +248,18 @@ def create_cli_agent(workspace_dir: str | None = None):
|
|
|
175
248
|
"/memory/": mem_backend,
|
|
176
249
|
},
|
|
177
250
|
)
|
|
178
|
-
mw = [
|
|
179
|
-
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
180
|
-
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
181
|
-
]
|
|
182
|
-
kwargs = dict(
|
|
183
|
-
_AGENT_KWARGS,
|
|
184
|
-
backend=be,
|
|
185
|
-
middleware=mw,
|
|
186
|
-
)
|
|
187
251
|
else:
|
|
188
|
-
|
|
252
|
+
be = backend
|
|
253
|
+
|
|
254
|
+
mw = [
|
|
255
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
256
|
+
create_skills_middleware(be),
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
# Re-load MCP tools from current config (picks up /mcp add changes)
|
|
260
|
+
kwargs = load_mcp_and_build_kwargs(be, mw)
|
|
189
261
|
|
|
190
262
|
return create_deep_agent(
|
|
191
263
|
**kwargs,
|
|
192
|
-
checkpointer=
|
|
264
|
+
checkpointer=checkpointer,
|
|
193
265
|
).with_config({"recursion_limit": 500})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""EvoScientist Agent - AI-powered research and code execution.
|
|
2
|
+
|
|
3
|
+
This package exposes a convenience API at the package root while keeping
|
|
4
|
+
imports lazy, so lightweight modules (for example config helpers) can be used
|
|
5
|
+
without importing heavy runtime dependencies.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from importlib import import_module
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_EXPORTS: dict[str, tuple[str, str]] = {
|
|
14
|
+
# Agent graph (lazy to avoid expensive initialization at import time)
|
|
15
|
+
"EvoScientist_agent": (".EvoScientist", "EvoScientist_agent"),
|
|
16
|
+
"create_cli_agent": (".EvoScientist", "create_cli_agent"),
|
|
17
|
+
# Backends
|
|
18
|
+
"CustomSandboxBackend": (".backends", "CustomSandboxBackend"),
|
|
19
|
+
"ReadOnlyFilesystemBackend": (".backends", "ReadOnlyFilesystemBackend"),
|
|
20
|
+
# Configuration
|
|
21
|
+
"EvoScientistConfig": (".config", "EvoScientistConfig"),
|
|
22
|
+
"load_config": (".config", "load_config"),
|
|
23
|
+
"save_config": (".config", "save_config"),
|
|
24
|
+
"get_effective_config": (".config", "get_effective_config"),
|
|
25
|
+
"get_config_path": (".config", "get_config_path"),
|
|
26
|
+
# LLM
|
|
27
|
+
"get_chat_model": (".llm", "get_chat_model"),
|
|
28
|
+
"MODELS": (".llm", "MODELS"),
|
|
29
|
+
"list_models": (".llm", "list_models"),
|
|
30
|
+
"DEFAULT_MODEL": (".llm", "DEFAULT_MODEL"),
|
|
31
|
+
# Middleware
|
|
32
|
+
"create_skills_middleware": (".middleware", "create_skills_middleware"),
|
|
33
|
+
# Prompts
|
|
34
|
+
"get_system_prompt": (".prompts", "get_system_prompt"),
|
|
35
|
+
"RESEARCHER_INSTRUCTIONS": (".prompts", "RESEARCHER_INSTRUCTIONS"),
|
|
36
|
+
# Tools
|
|
37
|
+
"tavily_search": (".tools", "tavily_search"),
|
|
38
|
+
"think_tool": (".tools", "think_tool"),
|
|
39
|
+
# Sessions
|
|
40
|
+
"get_checkpointer": (".sessions", "get_checkpointer"),
|
|
41
|
+
"generate_thread_id": (".sessions", "generate_thread_id"),
|
|
42
|
+
"list_threads": (".sessions", "list_threads"),
|
|
43
|
+
"delete_thread": (".sessions", "delete_thread"),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def __getattr__(name: str):
|
|
48
|
+
target = _EXPORTS.get(name)
|
|
49
|
+
if target is None:
|
|
50
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
51
|
+
|
|
52
|
+
module_name, attr_name = target
|
|
53
|
+
module = import_module(module_name, package=__name__)
|
|
54
|
+
value = getattr(module, attr_name)
|
|
55
|
+
# Cache after first load to avoid repeated import lookups.
|
|
56
|
+
globals()[name] = value
|
|
57
|
+
return value
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def __dir__() -> list[str]:
|
|
61
|
+
return sorted(set(globals()) | set(_EXPORTS))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
__all__ = list(_EXPORTS)
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
|
-
import
|
|
5
|
+
import uuid
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
from deepagents.backends import FilesystemBackend
|
|
8
|
+
from deepagents.backends import FilesystemBackend, LocalShellBackend
|
|
9
9
|
from deepagents.backends.filesystem import WriteResult, EditResult
|
|
10
10
|
from deepagents.backends.protocol import (
|
|
11
|
+
BackendProtocol,
|
|
11
12
|
ExecuteResponse,
|
|
12
13
|
FileDownloadResponse,
|
|
13
14
|
FileUploadResponse,
|
|
14
|
-
SandboxBackendProtocol,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
# System path prefixes that should never appear in virtual paths.
|
|
@@ -132,7 +132,7 @@ class ReadOnlyFilesystemBackend(FilesystemBackend):
|
|
|
132
132
|
)
|
|
133
133
|
|
|
134
134
|
|
|
135
|
-
class MergedReadOnlyBackend:
|
|
135
|
+
class MergedReadOnlyBackend(BackendProtocol):
|
|
136
136
|
"""Read-only backend that merges two directories.
|
|
137
137
|
|
|
138
138
|
Reads from *primary* first (user skills in workspace/skills/),
|
|
@@ -205,27 +205,7 @@ class MergedReadOnlyBackend:
|
|
|
205
205
|
error="This directory is read-only. Edit operations are not permitted here."
|
|
206
206
|
)
|
|
207
207
|
|
|
208
|
-
# --
|
|
209
|
-
|
|
210
|
-
async def aread(self, file_path: str, offset: int = 0, limit: int = 2000) -> str:
|
|
211
|
-
return self.read(file_path, offset, limit)
|
|
212
|
-
|
|
213
|
-
async def als_info(self, path: str = "/") -> list:
|
|
214
|
-
return self.ls_info(path)
|
|
215
|
-
|
|
216
|
-
async def agrep_raw(self, pattern: str, path: str | None = None, glob: str | None = None) -> list:
|
|
217
|
-
return self.grep_raw(pattern, path, glob)
|
|
218
|
-
|
|
219
|
-
async def aglob_info(self, pattern: str, path: str = "/") -> list:
|
|
220
|
-
return self.glob_info(pattern, path)
|
|
221
|
-
|
|
222
|
-
async def awrite(self, file_path: str, content: str) -> WriteResult:
|
|
223
|
-
return self.write(file_path, content)
|
|
224
|
-
|
|
225
|
-
async def aedit(self, file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult:
|
|
226
|
-
return self.edit(file_path, old_string, new_string, replace_all)
|
|
227
|
-
|
|
228
|
-
# -- download / upload (required by BackendProtocol) --
|
|
208
|
+
# -- download / upload --
|
|
229
209
|
|
|
230
210
|
def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
|
|
231
211
|
"""Download files, trying primary then secondary."""
|
|
@@ -237,38 +217,34 @@ class MergedReadOnlyBackend:
|
|
|
237
217
|
responses.append(resp)
|
|
238
218
|
return responses
|
|
239
219
|
|
|
240
|
-
async def adownload_files(self, paths: list[str]) -> list[FileDownloadResponse]:
|
|
241
|
-
return self.download_files(paths)
|
|
242
|
-
|
|
243
220
|
def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
|
|
244
221
|
return [
|
|
245
222
|
FileUploadResponse(path=path, error="permission_denied")
|
|
246
223
|
for path, _ in files
|
|
247
224
|
]
|
|
248
225
|
|
|
249
|
-
async def aupload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
|
|
250
|
-
return self.upload_files(files)
|
|
251
|
-
|
|
252
226
|
|
|
253
|
-
class CustomSandboxBackend(
|
|
227
|
+
class CustomSandboxBackend(LocalShellBackend):
|
|
254
228
|
"""
|
|
255
|
-
Custom sandbox backend - inherits
|
|
229
|
+
Custom sandbox backend - inherits LocalShellBackend with added safety.
|
|
256
230
|
|
|
257
231
|
Features:
|
|
258
232
|
- Inherits all file operations (ls, read, write, edit, grep, glob)
|
|
259
|
-
-
|
|
260
|
-
-
|
|
261
|
-
-
|
|
233
|
+
- Inherits shell command execution with output truncation and timeout
|
|
234
|
+
- Adds command validation to prevent directory traversal and dangerous operations
|
|
235
|
+
- Adds path sanitization to auto-correct common LLM path mistakes
|
|
262
236
|
- Compatible with LangGraph checkpointer (no thread locks)
|
|
263
237
|
"""
|
|
264
238
|
|
|
265
239
|
def __init__(
|
|
266
240
|
self,
|
|
267
241
|
root_dir: str = ".",
|
|
242
|
+
*,
|
|
268
243
|
virtual_mode: bool = True,
|
|
269
|
-
working_dir: str | None = None,
|
|
270
244
|
timeout: int = 300,
|
|
271
|
-
|
|
245
|
+
max_output_bytes: int = 100_000,
|
|
246
|
+
env: dict[str, str] | None = None,
|
|
247
|
+
inherit_env: bool = True,
|
|
272
248
|
):
|
|
273
249
|
"""
|
|
274
250
|
Initialize custom sandbox backend.
|
|
@@ -276,19 +252,23 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
276
252
|
Args:
|
|
277
253
|
root_dir: File system root directory
|
|
278
254
|
virtual_mode: Whether to enable virtual path mode
|
|
279
|
-
working_dir: Working directory for command execution (defaults to root_dir)
|
|
280
255
|
timeout: Command execution timeout in seconds
|
|
281
|
-
|
|
256
|
+
max_output_bytes: Max output size before truncation (default 100KB)
|
|
257
|
+
env: Extra environment variables for subprocess
|
|
258
|
+
inherit_env: Whether to inherit parent process env (default True)
|
|
282
259
|
"""
|
|
283
|
-
super().__init__(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
260
|
+
super().__init__(
|
|
261
|
+
root_dir=root_dir,
|
|
262
|
+
virtual_mode=virtual_mode,
|
|
263
|
+
timeout=timeout,
|
|
264
|
+
max_output_bytes=max_output_bytes,
|
|
265
|
+
env=env,
|
|
266
|
+
inherit_env=inherit_env,
|
|
267
|
+
)
|
|
268
|
+
# Override parent's "local-" prefix with our own
|
|
269
|
+
self._sandbox_id = f"evosci-{uuid.uuid4().hex[:8]}"
|
|
290
270
|
# Ensure working directory exists
|
|
291
|
-
os.makedirs(self.
|
|
271
|
+
os.makedirs(str(self.cwd), exist_ok=True)
|
|
292
272
|
|
|
293
273
|
def _resolve_path(self, key: str) -> Path:
|
|
294
274
|
"""Resolve path with sanitization to prevent nested directories.
|
|
@@ -331,62 +311,20 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
331
311
|
- Access to paths outside workspace
|
|
332
312
|
- Dangerous system commands
|
|
333
313
|
|
|
334
|
-
|
|
335
|
-
command: Command string to execute
|
|
336
|
-
|
|
337
|
-
Returns:
|
|
338
|
-
ExecuteResponse containing output, exit_code, and truncated flag
|
|
314
|
+
Then delegates to LocalShellBackend.execute() for actual execution.
|
|
339
315
|
"""
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if error:
|
|
344
|
-
return ExecuteResponse(
|
|
345
|
-
output=error,
|
|
346
|
-
exit_code=1,
|
|
347
|
-
truncated=False,
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
# Convert virtual paths to relative paths
|
|
351
|
-
if self.virtual_mode:
|
|
352
|
-
command = convert_virtual_paths_in_command(command=command)
|
|
353
|
-
|
|
354
|
-
result = subprocess.run(
|
|
355
|
-
command,
|
|
356
|
-
shell=True,
|
|
357
|
-
executable=self.shell,
|
|
358
|
-
cwd=self.working_dir,
|
|
359
|
-
capture_output=True,
|
|
360
|
-
text=True,
|
|
361
|
-
timeout=self.timeout,
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
output = ""
|
|
365
|
-
if result.stdout:
|
|
366
|
-
output += result.stdout
|
|
367
|
-
if result.stderr:
|
|
368
|
-
output += result.stderr
|
|
369
|
-
|
|
316
|
+
# Validate command safety
|
|
317
|
+
error = validate_command(command)
|
|
318
|
+
if error:
|
|
370
319
|
return ExecuteResponse(
|
|
371
|
-
output=
|
|
372
|
-
exit_code=
|
|
320
|
+
output=error,
|
|
321
|
+
exit_code=1,
|
|
373
322
|
truncated=False,
|
|
374
323
|
)
|
|
375
324
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
exit_code=-1,
|
|
380
|
-
truncated=False,
|
|
381
|
-
)
|
|
382
|
-
except Exception as e:
|
|
383
|
-
return ExecuteResponse(
|
|
384
|
-
output=f"Error executing command: {str(e)}",
|
|
385
|
-
exit_code=-1,
|
|
386
|
-
truncated=False,
|
|
387
|
-
)
|
|
325
|
+
# Convert virtual paths to relative paths
|
|
326
|
+
if self.virtual_mode:
|
|
327
|
+
command = convert_virtual_paths_in_command(command=command)
|
|
388
328
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
import asyncio
|
|
392
|
-
return await asyncio.to_thread(self.execute, command)
|
|
329
|
+
# Delegate to parent for subprocess execution
|
|
330
|
+
return super().execute(command)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Communication channels for EvoScientist.
|
|
2
|
+
|
|
3
|
+
This module provides an extensible interface for different messaging channels
|
|
4
|
+
(iMessage, WeChat, etc.) to communicate with the EvoScientist agent.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base import Channel, IncomingMessage, OutgoingMessage
|
|
8
|
+
|
|
9
|
+
__all__ = ["Channel", "IncomingMessage", "OutgoingMessage"]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Abstract base class for communication channels.
|
|
2
|
+
|
|
3
|
+
This module defines the Channel interface that all messaging channels
|
|
4
|
+
(iMessage, WeChat, etc.) must implement.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import AsyncIterator
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class IncomingMessage:
|
|
15
|
+
"""Represents a message received from a channel."""
|
|
16
|
+
|
|
17
|
+
sender: str # Phone number, email, or unique identifier
|
|
18
|
+
content: str # Message text content
|
|
19
|
+
timestamp: datetime # When the message was sent
|
|
20
|
+
message_id: str # Unique identifier for the message
|
|
21
|
+
metadata: dict = field(default_factory=dict) # Channel-specific metadata
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class OutgoingMessage:
|
|
26
|
+
"""Represents a message to be sent through a channel."""
|
|
27
|
+
|
|
28
|
+
recipient: str # Phone number, email, or unique identifier
|
|
29
|
+
content: str # Message text content
|
|
30
|
+
reply_to: str | None = None # Optional message ID being replied to
|
|
31
|
+
metadata: dict = field(default_factory=dict) # Channel-specific metadata
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Channel(ABC):
|
|
35
|
+
"""Abstract base class for messaging channels.
|
|
36
|
+
|
|
37
|
+
Subclasses must implement:
|
|
38
|
+
- start(): Initialize the channel (connect, authenticate, etc.)
|
|
39
|
+
- stop(): Clean up resources
|
|
40
|
+
- receive(): Async iterator yielding incoming messages
|
|
41
|
+
- send(): Send a message through the channel
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
async def start(self) -> None:
|
|
46
|
+
"""Initialize and start the channel.
|
|
47
|
+
|
|
48
|
+
This method should:
|
|
49
|
+
- Establish connections
|
|
50
|
+
- Verify permissions/authentication
|
|
51
|
+
- Start any background tasks needed
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
ChannelError: If initialization fails
|
|
55
|
+
"""
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
async def stop(self) -> None:
|
|
60
|
+
"""Stop the channel and clean up resources.
|
|
61
|
+
|
|
62
|
+
This method should:
|
|
63
|
+
- Close connections
|
|
64
|
+
- Cancel background tasks
|
|
65
|
+
- Release any held resources
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
async def receive(self) -> AsyncIterator[IncomingMessage]:
|
|
71
|
+
"""Async iterator that yields incoming messages.
|
|
72
|
+
|
|
73
|
+
Yields:
|
|
74
|
+
IncomingMessage: Each new message received
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
async for msg in channel.receive():
|
|
78
|
+
print(f"From {msg.sender}: {msg.content}")
|
|
79
|
+
"""
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
async def send(self, message: OutgoingMessage) -> bool:
|
|
84
|
+
"""Send a message through the channel.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
message: The message to send
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
True if sent successfully, False otherwise
|
|
91
|
+
"""
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ChannelError(Exception):
|
|
96
|
+
"""Base exception for channel-related errors."""
|
|
97
|
+
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ChannelPermissionError(ChannelError):
|
|
102
|
+
"""Raised when the channel lacks required permissions."""
|
|
103
|
+
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class ChannelConnectionError(ChannelError):
|
|
108
|
+
"""Raised when the channel cannot establish a connection."""
|
|
109
|
+
|
|
110
|
+
pass
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""iMessage channel implementation for EvoScientist.
|
|
2
|
+
|
|
3
|
+
Uses imsg CLI via JSON-RPC for real-time message streaming.
|
|
4
|
+
|
|
5
|
+
Requirements:
|
|
6
|
+
- macOS only
|
|
7
|
+
- imsg CLI: brew install steipete/tap/imsg
|
|
8
|
+
- Full Disk Access permission
|
|
9
|
+
- Messages.app logged into iCloud
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .channel_rpc import IMessageChannelRpc as IMessageChannel
|
|
13
|
+
from .channel_rpc import IMessageConfig
|
|
14
|
+
from .probe import probe_imessage, ProbeResult
|
|
15
|
+
from .targets import (
|
|
16
|
+
parse_target,
|
|
17
|
+
normalize_handle,
|
|
18
|
+
normalize_e164,
|
|
19
|
+
IMessageTarget,
|
|
20
|
+
IMessageService,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"IMessageChannel",
|
|
25
|
+
"IMessageConfig",
|
|
26
|
+
"probe_imessage",
|
|
27
|
+
"ProbeResult",
|
|
28
|
+
"parse_target",
|
|
29
|
+
"normalize_handle",
|
|
30
|
+
"normalize_e164",
|
|
31
|
+
"IMessageTarget",
|
|
32
|
+
"IMessageService",
|
|
33
|
+
]
|