lingtai 0.1.0__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.
Files changed (148) hide show
  1. lingtai-0.1.0/PKG-INFO +28 -0
  2. lingtai-0.1.0/pyproject.toml +42 -0
  3. lingtai-0.1.0/setup.cfg +4 -0
  4. lingtai-0.1.0/src/lingtai/__init__.py +64 -0
  5. lingtai-0.1.0/src/lingtai/__main__.py +4 -0
  6. lingtai-0.1.0/src/lingtai/addons/__init__.py +48 -0
  7. lingtai-0.1.0/src/lingtai/addons/imap/__init__.py +161 -0
  8. lingtai-0.1.0/src/lingtai/addons/imap/account.py +1167 -0
  9. lingtai-0.1.0/src/lingtai/addons/imap/config.example.json +26 -0
  10. lingtai-0.1.0/src/lingtai/addons/imap/manager.py +689 -0
  11. lingtai-0.1.0/src/lingtai/addons/imap/service.py +84 -0
  12. lingtai-0.1.0/src/lingtai/addons/telegram/__init__.py +119 -0
  13. lingtai-0.1.0/src/lingtai/addons/telegram/account.py +278 -0
  14. lingtai-0.1.0/src/lingtai/addons/telegram/config.example.json +13 -0
  15. lingtai-0.1.0/src/lingtai/addons/telegram/manager.py +692 -0
  16. lingtai-0.1.0/src/lingtai/addons/telegram/service.py +67 -0
  17. lingtai-0.1.0/src/lingtai/agent.py +578 -0
  18. lingtai-0.1.0/src/lingtai/capabilities/__init__.py +70 -0
  19. lingtai-0.1.0/src/lingtai/capabilities/avatar.py +310 -0
  20. lingtai-0.1.0/src/lingtai/capabilities/bash.py +253 -0
  21. lingtai-0.1.0/src/lingtai/capabilities/bash_policy.json +12 -0
  22. lingtai-0.1.0/src/lingtai/capabilities/compose.py +104 -0
  23. lingtai-0.1.0/src/lingtai/capabilities/daemon.py +395 -0
  24. lingtai-0.1.0/src/lingtai/capabilities/draw.py +90 -0
  25. lingtai-0.1.0/src/lingtai/capabilities/edit.py +68 -0
  26. lingtai-0.1.0/src/lingtai/capabilities/email.py +1036 -0
  27. lingtai-0.1.0/src/lingtai/capabilities/glob.py +49 -0
  28. lingtai-0.1.0/src/lingtai/capabilities/grep.py +53 -0
  29. lingtai-0.1.0/src/lingtai/capabilities/library.py +339 -0
  30. lingtai-0.1.0/src/lingtai/capabilities/listen.py +271 -0
  31. lingtai-0.1.0/src/lingtai/capabilities/psyche.py +222 -0
  32. lingtai-0.1.0/src/lingtai/capabilities/read.py +57 -0
  33. lingtai-0.1.0/src/lingtai/capabilities/talk.py +116 -0
  34. lingtai-0.1.0/src/lingtai/capabilities/vision.py +103 -0
  35. lingtai-0.1.0/src/lingtai/capabilities/web_read.py +75 -0
  36. lingtai-0.1.0/src/lingtai/capabilities/web_search.py +106 -0
  37. lingtai-0.1.0/src/lingtai/capabilities/write.py +49 -0
  38. lingtai-0.1.0/src/lingtai/cli.py +185 -0
  39. lingtai-0.1.0/src/lingtai/config_resolve.py +93 -0
  40. lingtai-0.1.0/src/lingtai/i18n/__init__.py +66 -0
  41. lingtai-0.1.0/src/lingtai/i18n/en.json +113 -0
  42. lingtai-0.1.0/src/lingtai/i18n/wen.json +113 -0
  43. lingtai-0.1.0/src/lingtai/i18n/zh.json +113 -0
  44. lingtai-0.1.0/src/lingtai/init_schema.py +176 -0
  45. lingtai-0.1.0/src/lingtai/llm/__init__.py +20 -0
  46. lingtai-0.1.0/src/lingtai/llm/_register.py +50 -0
  47. lingtai-0.1.0/src/lingtai/llm/anthropic/__init__.py +3 -0
  48. lingtai-0.1.0/src/lingtai/llm/anthropic/adapter.py +677 -0
  49. lingtai-0.1.0/src/lingtai/llm/anthropic/defaults.py +6 -0
  50. lingtai-0.1.0/src/lingtai/llm/api_gate.py +112 -0
  51. lingtai-0.1.0/src/lingtai/llm/base.py +104 -0
  52. lingtai-0.1.0/src/lingtai/llm/custom/__init__.py +3 -0
  53. lingtai-0.1.0/src/lingtai/llm/custom/adapter.py +39 -0
  54. lingtai-0.1.0/src/lingtai/llm/custom/defaults.py +6 -0
  55. lingtai-0.1.0/src/lingtai/llm/gemini/__init__.py +3 -0
  56. lingtai-0.1.0/src/lingtai/llm/gemini/adapter.py +909 -0
  57. lingtai-0.1.0/src/lingtai/llm/gemini/defaults.py +4 -0
  58. lingtai-0.1.0/src/lingtai/llm/interface_converters.py +280 -0
  59. lingtai-0.1.0/src/lingtai/llm/minimax/__init__.py +4 -0
  60. lingtai-0.1.0/src/lingtai/llm/minimax/adapter.py +50 -0
  61. lingtai-0.1.0/src/lingtai/llm/minimax/defaults.py +7 -0
  62. lingtai-0.1.0/src/lingtai/llm/minimax/mcp_client.py +149 -0
  63. lingtai-0.1.0/src/lingtai/llm/minimax/mcp_media_client.py +100 -0
  64. lingtai-0.1.0/src/lingtai/llm/openai/__init__.py +3 -0
  65. lingtai-0.1.0/src/lingtai/llm/openai/adapter.py +846 -0
  66. lingtai-0.1.0/src/lingtai/llm/openai/defaults.py +7 -0
  67. lingtai-0.1.0/src/lingtai/llm/service.py +264 -0
  68. lingtai-0.1.0/src/lingtai/network.py +327 -0
  69. lingtai-0.1.0/src/lingtai/services/__init__.py +1 -0
  70. lingtai-0.1.0/src/lingtai/services/file_io.py +134 -0
  71. lingtai-0.1.0/src/lingtai/services/image_gen/__init__.py +79 -0
  72. lingtai-0.1.0/src/lingtai/services/image_gen/gemini.py +97 -0
  73. lingtai-0.1.0/src/lingtai/services/image_gen/minimax.py +116 -0
  74. lingtai-0.1.0/src/lingtai/services/mail.py +4 -0
  75. lingtai-0.1.0/src/lingtai/services/mcp.py +254 -0
  76. lingtai-0.1.0/src/lingtai/services/music_gen/__init__.py +79 -0
  77. lingtai-0.1.0/src/lingtai/services/music_gen/minimax.py +162 -0
  78. lingtai-0.1.0/src/lingtai/services/transcription/__init__.py +69 -0
  79. lingtai-0.1.0/src/lingtai/services/transcription/gemini.py +95 -0
  80. lingtai-0.1.0/src/lingtai/services/transcription/whisper.py +80 -0
  81. lingtai-0.1.0/src/lingtai/services/tts/__init__.py +68 -0
  82. lingtai-0.1.0/src/lingtai/services/tts/gemini.py +128 -0
  83. lingtai-0.1.0/src/lingtai/services/tts/minimax.py +150 -0
  84. lingtai-0.1.0/src/lingtai/services/vision/__init__.py +103 -0
  85. lingtai-0.1.0/src/lingtai/services/vision/anthropic.py +61 -0
  86. lingtai-0.1.0/src/lingtai/services/vision/gemini.py +53 -0
  87. lingtai-0.1.0/src/lingtai/services/vision/local.py +78 -0
  88. lingtai-0.1.0/src/lingtai/services/vision/minimax.py +90 -0
  89. lingtai-0.1.0/src/lingtai/services/vision/openai.py +56 -0
  90. lingtai-0.1.0/src/lingtai/services/web_read.py +73 -0
  91. lingtai-0.1.0/src/lingtai/services/websearch/__init__.py +110 -0
  92. lingtai-0.1.0/src/lingtai/services/websearch/anthropic.py +68 -0
  93. lingtai-0.1.0/src/lingtai/services/websearch/duckduckgo.py +31 -0
  94. lingtai-0.1.0/src/lingtai/services/websearch/gemini.py +69 -0
  95. lingtai-0.1.0/src/lingtai/services/websearch/minimax.py +103 -0
  96. lingtai-0.1.0/src/lingtai/services/websearch/openai.py +58 -0
  97. lingtai-0.1.0/src/lingtai/venv_resolve.py +110 -0
  98. lingtai-0.1.0/src/lingtai.egg-info/PKG-INFO +28 -0
  99. lingtai-0.1.0/src/lingtai.egg-info/SOURCES.txt +146 -0
  100. lingtai-0.1.0/src/lingtai.egg-info/dependency_links.txt +1 -0
  101. lingtai-0.1.0/src/lingtai.egg-info/entry_points.txt +2 -0
  102. lingtai-0.1.0/src/lingtai.egg-info/requires.txt +29 -0
  103. lingtai-0.1.0/src/lingtai.egg-info/top_level.txt +2 -0
  104. lingtai-0.1.0/tests/test_adapter_registry.py +68 -0
  105. lingtai-0.1.0/tests/test_addon_imap_account.py +1009 -0
  106. lingtai-0.1.0/tests/test_addon_imap_manager.py +469 -0
  107. lingtai-0.1.0/tests/test_addon_imap_service.py +144 -0
  108. lingtai-0.1.0/tests/test_addon_telegram_account.py +207 -0
  109. lingtai-0.1.0/tests/test_addon_telegram_manager.py +444 -0
  110. lingtai-0.1.0/tests/test_addon_telegram_service.py +77 -0
  111. lingtai-0.1.0/tests/test_addons.py +51 -0
  112. lingtai-0.1.0/tests/test_agent.py +591 -0
  113. lingtai-0.1.0/tests/test_agent_capabilities.py +105 -0
  114. lingtai-0.1.0/tests/test_api_gate.py +134 -0
  115. lingtai-0.1.0/tests/test_cli.py +286 -0
  116. lingtai-0.1.0/tests/test_cli_integration.py +114 -0
  117. lingtai-0.1.0/tests/test_compaction.py +292 -0
  118. lingtai-0.1.0/tests/test_daemon.py +375 -0
  119. lingtai-0.1.0/tests/test_deep_refresh.py +261 -0
  120. lingtai-0.1.0/tests/test_eigen.py +261 -0
  121. lingtai-0.1.0/tests/test_git_init.py +99 -0
  122. lingtai-0.1.0/tests/test_handshake.py +60 -0
  123. lingtai-0.1.0/tests/test_init_schema.py +222 -0
  124. lingtai-0.1.0/tests/test_intrinsics_comm.py +76 -0
  125. lingtai-0.1.0/tests/test_karma.py +224 -0
  126. lingtai-0.1.0/tests/test_layers_avatar.py +197 -0
  127. lingtai-0.1.0/tests/test_layers_bash.py +203 -0
  128. lingtai-0.1.0/tests/test_layers_compose.py +163 -0
  129. lingtai-0.1.0/tests/test_layers_draw.py +209 -0
  130. lingtai-0.1.0/tests/test_layers_email.py +1470 -0
  131. lingtai-0.1.0/tests/test_layers_file.py +151 -0
  132. lingtai-0.1.0/tests/test_layers_listen.py +314 -0
  133. lingtai-0.1.0/tests/test_layers_talk.py +112 -0
  134. lingtai-0.1.0/tests/test_library.py +368 -0
  135. lingtai-0.1.0/tests/test_llm_service.py +43 -0
  136. lingtai-0.1.0/tests/test_mail_intrinsic.py +526 -0
  137. lingtai-0.1.0/tests/test_memory.py +212 -0
  138. lingtai-0.1.0/tests/test_network.py +419 -0
  139. lingtai-0.1.0/tests/test_override_intrinsic.py +59 -0
  140. lingtai-0.1.0/tests/test_psyche.py +328 -0
  141. lingtai-0.1.0/tests/test_services_file_io.py +94 -0
  142. lingtai-0.1.0/tests/test_services_integration.py +229 -0
  143. lingtai-0.1.0/tests/test_services_mail.py +303 -0
  144. lingtai-0.1.0/tests/test_silence_kill.py +190 -0
  145. lingtai-0.1.0/tests/test_system.py +278 -0
  146. lingtai-0.1.0/tests/test_three_agent_email.py +306 -0
  147. lingtai-0.1.0/tests/test_vision_capability.py +151 -0
  148. lingtai-0.1.0/tests/test_web_search_capability.py +111 -0
lingtai-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: lingtai
3
+ Version: 0.1.0
4
+ Summary: Generic research agent with intrinsic tools and MCP-compatible extension interface
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: lingtai-kernel>=0.1.0
8
+ Requires-Dist: httpx>=0.27
9
+ Provides-Extra: minimax
10
+ Requires-Dist: openai>=1.0; extra == "minimax"
11
+ Provides-Extra: gemini
12
+ Requires-Dist: google-genai>=1.0; extra == "gemini"
13
+ Provides-Extra: openai
14
+ Requires-Dist: openai>=1.0; extra == "openai"
15
+ Provides-Extra: anthropic
16
+ Requires-Dist: anthropic>=0.40; extra == "anthropic"
17
+ Provides-Extra: mcp
18
+ Requires-Dist: mcp>=1.0; extra == "mcp"
19
+ Provides-Extra: search
20
+ Requires-Dist: ddgs>=7.0; extra == "search"
21
+ Requires-Dist: trafilatura>=2.0; extra == "search"
22
+ Provides-Extra: all
23
+ Requires-Dist: google-genai>=1.0; extra == "all"
24
+ Requires-Dist: openai>=1.0; extra == "all"
25
+ Requires-Dist: anthropic>=0.40; extra == "all"
26
+ Requires-Dist: mcp>=1.0; extra == "all"
27
+ Requires-Dist: ddgs>=7.0; extra == "all"
28
+ Requires-Dist: trafilatura>=2.0; extra == "all"
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lingtai"
7
+ version = "0.1.0"
8
+ description = "Generic research agent with intrinsic tools and MCP-compatible extension interface"
9
+ requires-python = ">=3.11"
10
+ license = "MIT"
11
+ dependencies = [
12
+ "lingtai-kernel>=0.1.0",
13
+ "httpx>=0.27",
14
+ ]
15
+
16
+ [project.optional-dependencies]
17
+ minimax = ["openai>=1.0"]
18
+ gemini = ["google-genai>=1.0"]
19
+ openai = ["openai>=1.0"]
20
+ anthropic = ["anthropic>=0.40"]
21
+ mcp = ["mcp>=1.0"]
22
+ search = ["ddgs>=7.0", "trafilatura>=2.0"]
23
+ all = [
24
+ "google-genai>=1.0",
25
+ "openai>=1.0",
26
+ "anthropic>=0.40",
27
+ "mcp>=1.0",
28
+ "ddgs>=7.0",
29
+ "trafilatura>=2.0",
30
+ ]
31
+
32
+ [project.scripts]
33
+ lingtai = "lingtai.cli:main"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+
38
+ [tool.setuptools.package-data]
39
+ lingtai = ["i18n/*.json", "capabilities/*.json", "addons/*/*.json"]
40
+
41
+ [tool.pytest.ini_options]
42
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,64 @@
1
+ """lingtai — generic AI agent framework with intrinsic tools, composable capabilities, and pluggable services."""
2
+
3
+ from lingtai_kernel.types import UnknownToolError
4
+ from lingtai_kernel.config import AgentConfig
5
+ from lingtai_kernel.base_agent import BaseAgent
6
+ from .agent import Agent
7
+ from lingtai_kernel.state import AgentState
8
+ from lingtai_kernel.message import Message, MSG_REQUEST, MSG_USER_INPUT
9
+
10
+ # Capabilities
11
+ from .capabilities import setup_capability
12
+ from .capabilities.bash import BashManager
13
+ from .capabilities.avatar import AvatarManager
14
+ from .capabilities.email import EmailManager
15
+
16
+ # Services
17
+ from .services.file_io import FileIOService, LocalFileIOService, GrepMatch
18
+ from lingtai_kernel.services.mail import MailService, FilesystemMailService
19
+ from lingtai_kernel.services.logging import LoggingService, JSONLLoggingService
20
+ from .services.vision import VisionService, create_vision_service
21
+ from .services.websearch import SearchService, SearchResult, create_search_service
22
+ from .services.tts import TTSService, create_tts_service
23
+ from .services.image_gen import ImageGenService, create_image_gen_service
24
+ from .services.transcription import TranscriptionService, TranscriptionResult, create_transcription_service
25
+ from .services.music_gen import MusicGenService, create_music_gen_service
26
+
27
+ __all__ = [
28
+ # Core
29
+ "BaseAgent",
30
+ "Agent",
31
+ "Message",
32
+ "AgentState",
33
+ "MSG_REQUEST",
34
+ "MSG_USER_INPUT",
35
+ "AgentConfig",
36
+ "UnknownToolError",
37
+ # Capabilities
38
+ "setup_capability",
39
+ "BashManager",
40
+ "AvatarManager",
41
+ "EmailManager",
42
+ # Services
43
+ "FileIOService",
44
+ "LocalFileIOService",
45
+ "GrepMatch",
46
+ "MailService",
47
+ "FilesystemMailService",
48
+ "LoggingService",
49
+ "JSONLLoggingService",
50
+ "VisionService",
51
+ "create_vision_service",
52
+ "SearchService",
53
+ "SearchResult",
54
+ "create_search_service",
55
+ "TTSService",
56
+ "create_tts_service",
57
+ "ImageGenService",
58
+ "create_image_gen_service",
59
+ "TranscriptionService",
60
+ "TranscriptionResult",
61
+ "create_transcription_service",
62
+ "MusicGenService",
63
+ "create_music_gen_service",
64
+ ]
@@ -0,0 +1,4 @@
1
+ """Allow ``python -m lingtai`` to work like the ``lingtai`` console script."""
2
+ from lingtai.cli import main
3
+
4
+ main()
@@ -0,0 +1,48 @@
1
+ """Add-ons --- optional extensions that may depend on capabilities.
2
+
3
+ Add-ons are set up after capabilities. They use the same
4
+ setup(agent, **kwargs) interface but live separately to signal
5
+ they are optional and may have external dependencies.
6
+
7
+ Addon managers can implement start() and stop() methods for
8
+ lifecycle hooks --- called by Agent.start() and Agent.stop().
9
+
10
+ Usage:
11
+ agent = Agent(
12
+ capabilities=["email", "file"],
13
+ addons={"imap": {"email_address": "...", "email_password": "..."}},
14
+ )
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import importlib
19
+ from typing import TYPE_CHECKING, Any
20
+
21
+ if TYPE_CHECKING:
22
+ from lingtai_kernel.base_agent import BaseAgent
23
+
24
+ _BUILTIN: dict[str, str] = {
25
+ "imap": ".imap",
26
+ "telegram": ".telegram",
27
+ }
28
+
29
+
30
+ def setup_addon(agent: "BaseAgent", name: str, **kwargs: Any) -> Any:
31
+ """Look up an addon by name and call its setup(agent, **kwargs).
32
+
33
+ Returns whatever the addon's setup function returns (typically a manager).
34
+ Raises ValueError if the name is unknown.
35
+ """
36
+ module_path = _BUILTIN.get(name)
37
+ if module_path is None:
38
+ raise ValueError(
39
+ f"Unknown addon: {name!r}. "
40
+ f"Available: {', '.join(sorted(_BUILTIN))}"
41
+ )
42
+ mod = importlib.import_module(module_path, package=__package__)
43
+ setup_fn = getattr(mod, "setup", None)
44
+ if setup_fn is None:
45
+ raise ValueError(
46
+ f"Addon module {name!r} does not export a setup() function"
47
+ )
48
+ return setup_fn(agent, **kwargs)
@@ -0,0 +1,161 @@
1
+ """IMAP addon — real email via IMAP/SMTP.
2
+
3
+ Adds an `imap` tool with its own mailbox (working_dir/imap/).
4
+ An internal TCP bridge port lets other agents relay messages outward.
5
+
6
+ Usage (config file — recommended):
7
+ agent = Agent(
8
+ addons={"imap": {"config": "imap.json"}},
9
+ )
10
+
11
+ Usage (inline, single account):
12
+ agent = Agent(
13
+ addons={"imap": {
14
+ "email_address": "agent@example.com",
15
+ "email_password": "xxxx xxxx xxxx xxxx",
16
+ "imap_host": "imap.gmail.com",
17
+ "smtp_host": "smtp.gmail.com",
18
+ }},
19
+ )
20
+
21
+ Usage (inline, multi-account):
22
+ agent = Agent(
23
+ addons={"imap": {
24
+ "accounts": [
25
+ {"email_address": "a@gmail.com", "email_password": "xxxx"},
26
+ {"email_address": "b@outlook.com", "email_password": "yyyy",
27
+ "imap_host": "imap.outlook.com", "smtp_host": "smtp.outlook.com"},
28
+ ],
29
+ }},
30
+ )
31
+ """
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ import logging
36
+ from pathlib import Path
37
+ from typing import TYPE_CHECKING
38
+
39
+ from lingtai_kernel.services.mail import FilesystemMailService
40
+ from .manager import IMAPMailManager, SCHEMA, DESCRIPTION
41
+ from .service import IMAPMailService
42
+
43
+ if TYPE_CHECKING:
44
+ from lingtai_kernel.base_agent import BaseAgent
45
+
46
+ log = logging.getLogger(__name__)
47
+
48
+
49
+ def setup(
50
+ agent: "BaseAgent",
51
+ *,
52
+ # Config file
53
+ config: str | Path | None = None,
54
+ # Single-account shorthand
55
+ email_address: str | None = None,
56
+ email_password: str | None = None,
57
+ imap_host: str = "imap.gmail.com",
58
+ imap_port: int = 993,
59
+ smtp_host: str = "smtp.gmail.com",
60
+ smtp_port: int = 587,
61
+ allowed_senders: list[str] | None = None,
62
+ poll_interval: int = 30,
63
+ # Multi-account
64
+ accounts: list[dict] | None = None,
65
+ # Addon-level
66
+ bridge_port: int = 8399,
67
+ ) -> IMAPMailManager:
68
+ """Set up IMAP addon — registers imap tool, creates services.
69
+
70
+ Args:
71
+ config: Path to a JSON config file. Keys are the same as the kwargs.
72
+ Inline kwargs override config file values.
73
+
74
+ Accepts either a flat single-account config or a list of account dicts.
75
+
76
+ Listeners are NOT started here — they start in IMAPMailManager.start(),
77
+ which is called by Agent.start() via the addon lifecycle.
78
+ """
79
+ # Load config file if provided — inline kwargs override file values
80
+ if config is not None:
81
+ config_path = Path(config)
82
+ if not config_path.is_file():
83
+ raise FileNotFoundError(f"IMAP config not found: {config_path}")
84
+ from lingtai.config_resolve import load_jsonc
85
+ file_cfg = load_jsonc(config_path)
86
+ if accounts is None:
87
+ accounts = file_cfg.get("accounts")
88
+ if email_address is None:
89
+ email_address = file_cfg.get("email_address")
90
+ if email_password is None:
91
+ email_password = file_cfg.get("email_password")
92
+ if imap_host == "imap.gmail.com" and "imap_host" in file_cfg:
93
+ imap_host = file_cfg["imap_host"]
94
+ if imap_port == 993 and "imap_port" in file_cfg:
95
+ imap_port = file_cfg["imap_port"]
96
+ if smtp_host == "smtp.gmail.com" and "smtp_host" in file_cfg:
97
+ smtp_host = file_cfg["smtp_host"]
98
+ if smtp_port == 587 and "smtp_port" in file_cfg:
99
+ smtp_port = file_cfg["smtp_port"]
100
+ if allowed_senders is None:
101
+ allowed_senders = file_cfg.get("allowed_senders")
102
+ if poll_interval == 30 and "poll_interval" in file_cfg:
103
+ poll_interval = file_cfg["poll_interval"]
104
+ if bridge_port == 8399 and "bridge_port" in file_cfg:
105
+ bridge_port = file_cfg["bridge_port"]
106
+
107
+ if accounts is not None:
108
+ account_list = accounts
109
+ elif email_address is not None:
110
+ account_list = [{
111
+ "email_address": email_address,
112
+ "email_password": email_password,
113
+ "imap_host": imap_host,
114
+ "imap_port": imap_port,
115
+ "smtp_host": smtp_host,
116
+ "smtp_port": smtp_port,
117
+ "allowed_senders": allowed_senders,
118
+ "poll_interval": poll_interval,
119
+ }]
120
+ else:
121
+ raise ValueError(
122
+ "imap addon requires 'config', 'accounts', or 'email_address'"
123
+ )
124
+
125
+ working_dir = Path(agent._working_dir)
126
+ bridge_dir = working_dir / "imap_bridge"
127
+ bridge_dir.mkdir(parents=True, exist_ok=True)
128
+
129
+ imap_svc = IMAPMailService(
130
+ accounts=account_list,
131
+ working_dir=working_dir,
132
+ )
133
+
134
+ bridge = FilesystemMailService(working_dir=bridge_dir)
135
+
136
+ mgr = IMAPMailManager(agent, service=imap_svc, tcp_alias=str(bridge_dir))
137
+ mgr._bridge = bridge
138
+
139
+ # Build system prompt listing all configured accounts
140
+ addr_lines = "\n".join(
141
+ f" - {acct['email_address']}"
142
+ for acct in account_list
143
+ )
144
+ system_prompt = (
145
+ f"IMAP email accounts:\n{addr_lines}\n"
146
+ f"Internal bridge: {bridge_dir} "
147
+ f"(other agents can send to this address to relay via IMAP/SMTP)\n"
148
+ f"Use imap(action=...) for external email. "
149
+ f"Use email(action=...) for inter-agent communication."
150
+ )
151
+
152
+ agent.add_tool(
153
+ "imap", schema=SCHEMA, handler=mgr.handle, description=DESCRIPTION,
154
+ system_prompt=system_prompt,
155
+ )
156
+
157
+ log.info(
158
+ "IMAP addon configured: %d account(s) (bridge: %s)",
159
+ len(account_list), bridge_dir,
160
+ )
161
+ return mgr