EvoScientist 0.0.1.dev3__tar.gz → 0.0.1.dev5__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.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/EvoScientist.py +98 -41
- evoscientist-0.0.1.dev5/EvoScientist/__init__.py +60 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/backends.py +39 -32
- evoscientist-0.0.1.dev5/EvoScientist/channels/__init__.py +9 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/base.py +110 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/__init__.py +33 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/channel_rpc.py +427 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/probe.py +106 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/rpc_client.py +235 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/serve.py +428 -0
- evoscientist-0.0.1.dev5/EvoScientist/channels/imessage/targets.py +232 -0
- evoscientist-0.0.1.dev5/EvoScientist/cli.py +1653 -0
- evoscientist-0.0.1.dev5/EvoScientist/config.py +284 -0
- evoscientist-0.0.1.dev5/EvoScientist/llm/__init__.py +21 -0
- evoscientist-0.0.1.dev5/EvoScientist/llm/models.py +115 -0
- evoscientist-0.0.1.dev5/EvoScientist/mcp/__init__.py +29 -0
- evoscientist-0.0.1.dev5/EvoScientist/mcp/client.py +591 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/middleware.py +7 -21
- evoscientist-0.0.1.dev5/EvoScientist/onboard.py +1325 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/paths.py +18 -3
- evoscientist-0.0.1.dev5/EvoScientist/skills/find-skills/SKILL.md +76 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/SKILL.md +64 -58
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/__init__.py +2 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/display.py +210 -6
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/formatter.py +4 -1
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/utils.py +11 -2
- evoscientist-0.0.1.dev5/EvoScientist/tools/__init__.py +18 -0
- evoscientist-0.0.1.dev5/EvoScientist/tools/image.py +74 -0
- evoscientist-0.0.1.dev5/EvoScientist/tools/search.py +115 -0
- evoscientist-0.0.1.dev5/EvoScientist/tools/skill_manager.py +120 -0
- {evoscientist-0.0.1.dev3/EvoScientist → evoscientist-0.0.1.dev5/EvoScientist/tools}/skills_manager.py +106 -28
- evoscientist-0.0.1.dev5/EvoScientist/tools/think.py +32 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/PKG-INFO +225 -24
- evoscientist-0.0.1.dev5/EvoScientist.egg-info/SOURCES.txt +70 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/requires.txt +8 -1
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/PKG-INFO +225 -24
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/README.md +216 -22
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/pyproject.toml +16 -3
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/tests/test_backends.py +88 -3
- evoscientist-0.0.1.dev5/tests/test_cli_run_name.py +38 -0
- evoscientist-0.0.1.dev5/tests/test_config.py +364 -0
- evoscientist-0.0.1.dev5/tests/test_event_loop.py +200 -0
- evoscientist-0.0.1.dev5/tests/test_imports.py +36 -0
- evoscientist-0.0.1.dev5/tests/test_llm.py +242 -0
- evoscientist-0.0.1.dev5/tests/test_mcp_client.py +539 -0
- evoscientist-0.0.1.dev5/tests/test_onboard.py +688 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/tests/test_skills_manager.py +7 -11
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/tests/test_stream_state.py +161 -11
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/tests/test_stream_tracker.py +1 -1
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/tests/test_stream_utils.py +28 -4
- evoscientist-0.0.1.dev5/tests/test_tools.py +107 -0
- evoscientist-0.0.1.dev3/EvoScientist/__init__.py +0 -24
- evoscientist-0.0.1.dev3/EvoScientist/cli.py +0 -427
- evoscientist-0.0.1.dev3/EvoScientist/skills/accelerate/SKILL.md +0 -332
- evoscientist-0.0.1.dev3/EvoScientist/skills/accelerate/references/custom-plugins.md +0 -453
- evoscientist-0.0.1.dev3/EvoScientist/skills/accelerate/references/megatron-integration.md +0 -489
- evoscientist-0.0.1.dev3/EvoScientist/skills/accelerate/references/performance.md +0 -525
- evoscientist-0.0.1.dev3/EvoScientist/skills/bitsandbytes/SKILL.md +0 -411
- evoscientist-0.0.1.dev3/EvoScientist/skills/bitsandbytes/references/memory-optimization.md +0 -521
- evoscientist-0.0.1.dev3/EvoScientist/skills/bitsandbytes/references/qlora-training.md +0 -521
- evoscientist-0.0.1.dev3/EvoScientist/skills/bitsandbytes/references/quantization-formats.md +0 -447
- evoscientist-0.0.1.dev3/EvoScientist/skills/find-skills/SKILL.md +0 -133
- evoscientist-0.0.1.dev3/EvoScientist/skills/find-skills/scripts/install_skill.py +0 -211
- evoscientist-0.0.1.dev3/EvoScientist/skills/flash-attention/SKILL.md +0 -367
- evoscientist-0.0.1.dev3/EvoScientist/skills/flash-attention/references/benchmarks.md +0 -215
- evoscientist-0.0.1.dev3/EvoScientist/skills/flash-attention/references/transformers-integration.md +0 -293
- evoscientist-0.0.1.dev3/EvoScientist/skills/llama-cpp/SKILL.md +0 -258
- evoscientist-0.0.1.dev3/EvoScientist/skills/llama-cpp/references/optimization.md +0 -89
- evoscientist-0.0.1.dev3/EvoScientist/skills/llama-cpp/references/quantization.md +0 -213
- evoscientist-0.0.1.dev3/EvoScientist/skills/llama-cpp/references/server.md +0 -125
- evoscientist-0.0.1.dev3/EvoScientist/skills/lm-evaluation-harness/SKILL.md +0 -490
- evoscientist-0.0.1.dev3/EvoScientist/skills/lm-evaluation-harness/references/api-evaluation.md +0 -490
- evoscientist-0.0.1.dev3/EvoScientist/skills/lm-evaluation-harness/references/benchmark-guide.md +0 -488
- evoscientist-0.0.1.dev3/EvoScientist/skills/lm-evaluation-harness/references/custom-tasks.md +0 -602
- evoscientist-0.0.1.dev3/EvoScientist/skills/lm-evaluation-harness/references/distributed-eval.md +0 -519
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/SKILL.md +0 -937
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/references/checklists.md +0 -361
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/references/citation-workflow.md +0 -562
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/references/reviewer-guidelines.md +0 -367
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/references/sources.md +0 -159
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/references/writing-guide.md +0 -476
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/README.md +0 -251
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/aaai2026/README.md +0 -534
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +0 -144
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +0 -952
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bib +0 -111
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bst +0 -1493
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.sty +0 -315
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/README.md +0 -50
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/acl.sty +0 -312
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/acl_latex.tex +0 -377
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/acl_lualatex.tex +0 -101
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/acl_natbib.bst +0 -1940
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/anthology.bib.txt +0 -26
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/custom.bib +0 -70
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/acl/formatting.md +0 -326
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/README.md +0 -3
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bib +0 -11
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bst +0 -1440
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.sty +0 -218
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.tex +0 -305
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/fancyhdr.sty +0 -485
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/math_commands.tex +0 -508
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/colm2025/natbib.sty +0 -1246
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/fancyhdr.sty +0 -485
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bib +0 -24
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bst +0 -1440
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.sty +0 -246
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.tex +0 -414
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/math_commands.tex +0 -508
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/iclr2026/natbib.sty +0 -1246
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithm.sty +0 -79
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithmic.sty +0 -201
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.bib +0 -75
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.pdf +0 -0
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.tex +0 -662
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/fancyhdr.sty +0 -864
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.bst +0 -1443
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.sty +0 -767
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/neurips2025/Makefile +0 -36
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/neurips2025/extra_pkgs.tex +0 -53
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/neurips2025/main.tex +0 -38
- evoscientist-0.0.1.dev3/EvoScientist/skills/ml-paper-writing/templates/neurips2025/neurips.sty +0 -382
- evoscientist-0.0.1.dev3/EvoScientist/skills/peft/SKILL.md +0 -431
- evoscientist-0.0.1.dev3/EvoScientist/skills/peft/references/advanced-usage.md +0 -514
- evoscientist-0.0.1.dev3/EvoScientist/skills/peft/references/troubleshooting.md +0 -480
- evoscientist-0.0.1.dev3/EvoScientist/skills/ray-data/SKILL.md +0 -326
- evoscientist-0.0.1.dev3/EvoScientist/skills/ray-data/references/integration.md +0 -82
- evoscientist-0.0.1.dev3/EvoScientist/skills/ray-data/references/transformations.md +0 -83
- evoscientist-0.0.1.dev3/EvoScientist/skills/skill-creator/LICENSE.txt +0 -202
- evoscientist-0.0.1.dev3/EvoScientist/tools.py +0 -199
- evoscientist-0.0.1.dev3/EvoScientist.egg-info/SOURCES.txt +0 -125
- evoscientist-0.0.1.dev3/tests/test_imports.py +0 -59
- evoscientist-0.0.1.dev3/tests/test_tools.py +0 -18
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/__main__.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/memory.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/prompts.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/references/output-patterns.md +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/references/workflows.md +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/scripts/init_skill.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/scripts/package_skill.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/skills/skill-creator/scripts/quick_validate.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/emitter.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/events.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/state.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/stream/tracker.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/subagent.yaml +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist/utils.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/dependency_links.txt +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/entry_points.txt +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/EvoScientist.egg-info/top_level.txt +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/LICENSE +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/setup.cfg +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/tests/test_prompts.py +0 -0
- {evoscientist-0.0.1.dev3 → evoscientist-0.0.1.dev5}/tests/test_stream_emitter.py +0 -0
|
@@ -13,22 +13,24 @@ Usage:
|
|
|
13
13
|
...
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
import os
|
|
17
16
|
from datetime import datetime
|
|
18
17
|
from pathlib import Path
|
|
19
18
|
|
|
20
19
|
from deepagents import create_deep_agent
|
|
21
20
|
from deepagents.backends import FilesystemBackend, CompositeBackend
|
|
22
|
-
from langchain.chat_models import init_chat_model
|
|
23
21
|
|
|
24
22
|
from .backends import CustomSandboxBackend, MergedReadOnlyBackend
|
|
23
|
+
from .config import get_effective_config, apply_config_to_env
|
|
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
|
|
28
|
-
from .tools import tavily_search, think_tool, skill_manager
|
|
29
|
+
from .tools import tavily_search, think_tool, skill_manager, view_image
|
|
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
|
)
|
|
@@ -37,16 +39,21 @@ from .paths import (
|
|
|
37
39
|
# Configuration
|
|
38
40
|
# =============================================================================
|
|
39
41
|
|
|
42
|
+
# Load configuration from file/env/defaults
|
|
43
|
+
_config = get_effective_config()
|
|
44
|
+
apply_config_to_env(_config)
|
|
45
|
+
|
|
40
46
|
# Backend mode: "sandbox" (with execute) or "filesystem" (read/write only)
|
|
41
47
|
BACKEND_MODE = "sandbox"
|
|
42
48
|
|
|
43
|
-
# Research limits
|
|
44
|
-
MAX_CONCURRENT =
|
|
45
|
-
MAX_ITERATIONS =
|
|
49
|
+
# Research limits (from config)
|
|
50
|
+
MAX_CONCURRENT = _config.max_concurrent
|
|
51
|
+
MAX_ITERATIONS = _config.max_iterations
|
|
46
52
|
|
|
47
53
|
# Workspace settings
|
|
48
54
|
ensure_dirs()
|
|
49
55
|
WORKSPACE_DIR = str(default_workspace_dir())
|
|
56
|
+
set_active_workspace(WORKSPACE_DIR)
|
|
50
57
|
MEMORY_DIR = str(_MEMORY_DIR_PATH) # Shared across sessions (not per-session)
|
|
51
58
|
SKILLS_DIR = str(Path(__file__).parent / "skills")
|
|
52
59
|
USER_SKILLS_DIR = str(_USER_SKILLS_DIR_PATH)
|
|
@@ -65,11 +72,10 @@ SYSTEM_PROMPT = get_system_prompt(
|
|
|
65
72
|
max_iterations=MAX_ITERATIONS,
|
|
66
73
|
)
|
|
67
74
|
|
|
68
|
-
# Initialize chat model
|
|
69
|
-
chat_model =
|
|
70
|
-
model=
|
|
71
|
-
|
|
72
|
-
# thinking={"type": "enabled", "budget_tokens": 2000},
|
|
75
|
+
# Initialize chat model using the LLM module (respects config settings)
|
|
76
|
+
chat_model = get_chat_model(
|
|
77
|
+
model=_config.model,
|
|
78
|
+
provider=_config.provider,
|
|
73
79
|
)
|
|
74
80
|
|
|
75
81
|
# Initialize workspace backend based on mode
|
|
@@ -109,33 +115,84 @@ backend = CompositeBackend(
|
|
|
109
115
|
tool_registry = {
|
|
110
116
|
"think_tool": think_tool,
|
|
111
117
|
"tavily_search": tavily_search,
|
|
118
|
+
"view_image": view_image,
|
|
112
119
|
}
|
|
113
120
|
|
|
121
|
+
# Base tools that every agent variant gets (before MCP)
|
|
122
|
+
BASE_TOOLS = [think_tool, skill_manager, view_image]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _build_base_kwargs(base_backend, base_middleware):
|
|
126
|
+
"""Build agent kwargs *without* MCP (fast, no subprocess spawning)."""
|
|
127
|
+
subs = load_subagents(
|
|
128
|
+
SUBAGENTS_CONFIG,
|
|
129
|
+
tool_registry=tool_registry,
|
|
130
|
+
prompt_refs=prompt_refs,
|
|
131
|
+
)
|
|
132
|
+
return dict(
|
|
133
|
+
name="EvoScientist",
|
|
134
|
+
model=chat_model,
|
|
135
|
+
tools=list(BASE_TOOLS),
|
|
136
|
+
backend=base_backend,
|
|
137
|
+
subagents=subs,
|
|
138
|
+
middleware=base_middleware,
|
|
139
|
+
system_prompt=SYSTEM_PROMPT,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def load_mcp_and_build_kwargs(base_backend, base_middleware):
|
|
144
|
+
"""(Re-)load MCP tools and build agent kwargs.
|
|
145
|
+
|
|
146
|
+
Called on every ``create_cli_agent()`` call so that ``/new`` picks up
|
|
147
|
+
MCP config changes. Falls back to base kwargs if no MCP configured.
|
|
148
|
+
"""
|
|
149
|
+
mcp_by_agent = load_mcp_tools()
|
|
150
|
+
if not mcp_by_agent:
|
|
151
|
+
return _build_base_kwargs(base_backend, base_middleware)
|
|
152
|
+
|
|
153
|
+
# Fresh tool registry — start from base tools + MCP tools
|
|
154
|
+
registry = dict(tool_registry)
|
|
155
|
+
for tools in mcp_by_agent.values():
|
|
156
|
+
for t in tools:
|
|
157
|
+
registry[t.name] = t
|
|
158
|
+
|
|
159
|
+
mcp_main = mcp_by_agent.pop("main", [])
|
|
160
|
+
|
|
161
|
+
subs = load_subagents(
|
|
162
|
+
SUBAGENTS_CONFIG,
|
|
163
|
+
tool_registry=registry,
|
|
164
|
+
prompt_refs=prompt_refs,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Inject MCP tools into subagents by name
|
|
168
|
+
for sa in subs:
|
|
169
|
+
if sa_tools := mcp_by_agent.get(sa["name"], []):
|
|
170
|
+
sa.setdefault("tools", []).extend(sa_tools)
|
|
171
|
+
|
|
172
|
+
return dict(
|
|
173
|
+
name="EvoScientist",
|
|
174
|
+
model=chat_model,
|
|
175
|
+
tools=BASE_TOOLS + mcp_main,
|
|
176
|
+
backend=base_backend,
|
|
177
|
+
subagents=subs,
|
|
178
|
+
middleware=base_middleware,
|
|
179
|
+
system_prompt=SYSTEM_PROMPT,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
114
183
|
prompt_refs = {
|
|
115
184
|
"RESEARCHER_INSTRUCTIONS": RESEARCHER_INSTRUCTIONS.format(date=current_date),
|
|
116
185
|
}
|
|
117
186
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
187
|
+
base_middleware = [
|
|
188
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
189
|
+
create_skills_middleware(backend),
|
|
190
|
+
]
|
|
123
191
|
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
tools=[think_tool, skill_manager],
|
|
129
|
-
backend=backend,
|
|
130
|
-
subagents=subagents,
|
|
131
|
-
middleware=[
|
|
132
|
-
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
133
|
-
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
134
|
-
],
|
|
135
|
-
system_prompt=SYSTEM_PROMPT,
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
# Default agent (no checkpointer) — used by langgraph dev / LangSmith / notebooks
|
|
192
|
+
# Default agent (no checkpointer) — used by langgraph dev / LangSmith / notebooks.
|
|
193
|
+
# Built WITHOUT MCP at import time to avoid spawning subprocesses on every import.
|
|
194
|
+
# MCP tools are loaded on-demand in create_cli_agent().
|
|
195
|
+
_AGENT_KWARGS = _build_base_kwargs(backend, base_middleware)
|
|
139
196
|
EvoScientist_agent = create_deep_agent(**_AGENT_KWARGS).with_config({"recursion_limit": 500})
|
|
140
197
|
|
|
141
198
|
|
|
@@ -150,6 +207,7 @@ def create_cli_agent(workspace_dir: str | None = None):
|
|
|
150
207
|
from langgraph.checkpoint.memory import InMemorySaver # type: ignore[import-untyped]
|
|
151
208
|
|
|
152
209
|
if workspace_dir:
|
|
210
|
+
set_active_workspace(workspace_dir)
|
|
153
211
|
ws_backend = CustomSandboxBackend(
|
|
154
212
|
root_dir=workspace_dir,
|
|
155
213
|
virtual_mode=True,
|
|
@@ -171,17 +229,16 @@ def create_cli_agent(workspace_dir: str | None = None):
|
|
|
171
229
|
"/memory/": mem_backend,
|
|
172
230
|
},
|
|
173
231
|
)
|
|
174
|
-
mw = [
|
|
175
|
-
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
176
|
-
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
177
|
-
]
|
|
178
|
-
kwargs = dict(
|
|
179
|
-
_AGENT_KWARGS,
|
|
180
|
-
backend=be,
|
|
181
|
-
middleware=mw,
|
|
182
|
-
)
|
|
183
232
|
else:
|
|
184
|
-
|
|
233
|
+
be = backend
|
|
234
|
+
|
|
235
|
+
mw = [
|
|
236
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
237
|
+
create_skills_middleware(be),
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
# Re-load MCP tools from current config (picks up /mcp add changes)
|
|
241
|
+
kwargs = load_mcp_and_build_kwargs(be, mw)
|
|
185
242
|
|
|
186
243
|
return create_deep_agent(
|
|
187
244
|
**kwargs,
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
"view_image": (".tools", "view_image"),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def __getattr__(name: str):
|
|
44
|
+
target = _EXPORTS.get(name)
|
|
45
|
+
if target is None:
|
|
46
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
47
|
+
|
|
48
|
+
module_name, attr_name = target
|
|
49
|
+
module = import_module(module_name, package=__name__)
|
|
50
|
+
value = getattr(module, attr_name)
|
|
51
|
+
# Cache after first load to avoid repeated import lookups.
|
|
52
|
+
globals()[name] = value
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def __dir__() -> list[str]:
|
|
57
|
+
return sorted(set(globals()) | set(_EXPORTS))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = list(_EXPORTS)
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
5
|
import subprocess
|
|
6
|
+
import uuid
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
from deepagents.backends import FilesystemBackend
|
|
9
10
|
from deepagents.backends.filesystem import WriteResult, EditResult
|
|
10
11
|
from deepagents.backends.protocol import (
|
|
12
|
+
BackendProtocol,
|
|
11
13
|
ExecuteResponse,
|
|
12
14
|
FileDownloadResponse,
|
|
13
15
|
FileUploadResponse,
|
|
@@ -132,7 +134,7 @@ class ReadOnlyFilesystemBackend(FilesystemBackend):
|
|
|
132
134
|
)
|
|
133
135
|
|
|
134
136
|
|
|
135
|
-
class MergedReadOnlyBackend:
|
|
137
|
+
class MergedReadOnlyBackend(BackendProtocol):
|
|
136
138
|
"""Read-only backend that merges two directories.
|
|
137
139
|
|
|
138
140
|
Reads from *primary* first (user skills in workspace/skills/),
|
|
@@ -205,27 +207,7 @@ class MergedReadOnlyBackend:
|
|
|
205
207
|
error="This directory is read-only. Edit operations are not permitted here."
|
|
206
208
|
)
|
|
207
209
|
|
|
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) --
|
|
210
|
+
# -- download / upload --
|
|
229
211
|
|
|
230
212
|
def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
|
|
231
213
|
"""Download files, trying primary then secondary."""
|
|
@@ -237,18 +219,12 @@ class MergedReadOnlyBackend:
|
|
|
237
219
|
responses.append(resp)
|
|
238
220
|
return responses
|
|
239
221
|
|
|
240
|
-
async def adownload_files(self, paths: list[str]) -> list[FileDownloadResponse]:
|
|
241
|
-
return self.download_files(paths)
|
|
242
|
-
|
|
243
222
|
def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
|
|
244
223
|
return [
|
|
245
224
|
FileUploadResponse(path=path, error="permission_denied")
|
|
246
225
|
for path, _ in files
|
|
247
226
|
]
|
|
248
227
|
|
|
249
|
-
async def aupload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
|
|
250
|
-
return self.upload_files(files)
|
|
251
|
-
|
|
252
228
|
|
|
253
229
|
class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
254
230
|
"""
|
|
@@ -269,6 +245,9 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
269
245
|
working_dir: str | None = None,
|
|
270
246
|
timeout: int = 300,
|
|
271
247
|
shell: str = "/bin/bash",
|
|
248
|
+
max_output_bytes: int = 100_000,
|
|
249
|
+
env: dict[str, str] | None = None,
|
|
250
|
+
inherit_env: bool = True,
|
|
272
251
|
):
|
|
273
252
|
"""
|
|
274
253
|
Initialize custom sandbox backend.
|
|
@@ -279,17 +258,33 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
279
258
|
working_dir: Working directory for command execution (defaults to root_dir)
|
|
280
259
|
timeout: Command execution timeout in seconds
|
|
281
260
|
shell: Shell program to use
|
|
261
|
+
max_output_bytes: Max output size before truncation (default 100KB)
|
|
262
|
+
env: Extra environment variables for subprocess
|
|
263
|
+
inherit_env: Whether to inherit parent process env (default True)
|
|
282
264
|
"""
|
|
283
265
|
super().__init__(root_dir=root_dir, virtual_mode=virtual_mode)
|
|
284
266
|
|
|
267
|
+
self._sandbox_id = f"evosci-{uuid.uuid4().hex[:8]}"
|
|
285
268
|
self.working_dir = working_dir or root_dir
|
|
286
269
|
self.timeout = timeout
|
|
287
270
|
self.shell = shell
|
|
288
271
|
self.virtual_mode = virtual_mode
|
|
272
|
+
self._max_output_bytes = max_output_bytes
|
|
273
|
+
|
|
274
|
+
# Build subprocess environment
|
|
275
|
+
if inherit_env:
|
|
276
|
+
self._env = {**os.environ, **(env or {})}
|
|
277
|
+
else:
|
|
278
|
+
self._env = dict(env) if env else {}
|
|
289
279
|
|
|
290
280
|
# Ensure working directory exists
|
|
291
281
|
os.makedirs(self.working_dir, exist_ok=True)
|
|
292
282
|
|
|
283
|
+
@property
|
|
284
|
+
def id(self) -> str:
|
|
285
|
+
"""Unique identifier for the sandbox backend instance."""
|
|
286
|
+
return self._sandbox_id
|
|
287
|
+
|
|
293
288
|
def _resolve_path(self, key: str) -> Path:
|
|
294
289
|
"""Resolve path with sanitization to prevent nested directories.
|
|
295
290
|
|
|
@@ -359,18 +354,30 @@ class CustomSandboxBackend(FilesystemBackend, SandboxBackendProtocol):
|
|
|
359
354
|
capture_output=True,
|
|
360
355
|
text=True,
|
|
361
356
|
timeout=self.timeout,
|
|
357
|
+
env=self._env,
|
|
362
358
|
)
|
|
363
359
|
|
|
364
|
-
|
|
360
|
+
output_parts = []
|
|
365
361
|
if result.stdout:
|
|
366
|
-
|
|
362
|
+
output_parts.append(result.stdout)
|
|
367
363
|
if result.stderr:
|
|
368
|
-
|
|
364
|
+
stderr_lines = result.stderr.strip().split("\n")
|
|
365
|
+
output_parts.extend(f"[stderr] {line}" for line in stderr_lines)
|
|
366
|
+
output = "\n".join(output_parts) if output_parts else ""
|
|
367
|
+
|
|
368
|
+
if result.returncode != 0:
|
|
369
|
+
output = f"{output.rstrip()}\n\nExit code: {result.returncode}"
|
|
370
|
+
|
|
371
|
+
truncated = False
|
|
372
|
+
if len(output) > self._max_output_bytes:
|
|
373
|
+
output = output[:self._max_output_bytes]
|
|
374
|
+
output += f"\n\n... Output truncated at {self._max_output_bytes} bytes."
|
|
375
|
+
truncated = True
|
|
369
376
|
|
|
370
377
|
return ExecuteResponse(
|
|
371
378
|
output=output,
|
|
372
379
|
exit_code=result.returncode,
|
|
373
|
-
truncated=
|
|
380
|
+
truncated=truncated,
|
|
374
381
|
)
|
|
375
382
|
|
|
376
383
|
except subprocess.TimeoutExpired:
|
|
@@ -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
|
+
]
|