hyperagents 1.2.3rc5.post3__py3-none-any.whl

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 (361) hide show
  1. agents/__init__.py +31 -0
  2. agents/background_monitor.py +453 -0
  3. agents/learning.py +460 -0
  4. agents/planner.py +421 -0
  5. agents/transcriber.py +341 -0
  6. civilization/__init__.py +51 -0
  7. civilization/api/__init__.py +7 -0
  8. civilization/api/civilization_api.py +361 -0
  9. civilization/graph/__init__.py +19 -0
  10. civilization/graph/knowledge_linker.py +348 -0
  11. civilization/graph/org_graph.py +348 -0
  12. civilization/graph/process_graph.py +386 -0
  13. civilization/ingestion/__init__.py +20 -0
  14. civilization/ingestion/chunker.py +273 -0
  15. civilization/ingestion/document_ingestor.py +358 -0
  16. civilization/ingestion/embedder.py +262 -0
  17. civilization/ingestion/extractor.py +224 -0
  18. civilization/interview/__init__.py +19 -0
  19. civilization/interview/gap_detector.py +380 -0
  20. civilization/interview/interviewer.py +323 -0
  21. civilization/interview/questioner.py +363 -0
  22. civilization/retrieval/__init__.py +17 -0
  23. civilization/retrieval/civilization_rag.py +316 -0
  24. civilization/retrieval/context_injector.py +342 -0
  25. civilization/retrieval/relevance_ranker.py +287 -0
  26. civilization/schema.py +299 -0
  27. civilization/store.py +177 -0
  28. civilization/sync/__init__.py +16 -0
  29. civilization/sync/confluence_sync.py +317 -0
  30. civilization/sync/gdrive_sync.py +262 -0
  31. civilization/sync/notion_sync.py +237 -0
  32. civilization/versioning/__init__.py +19 -0
  33. civilization/versioning/diff_engine.py +351 -0
  34. civilization/versioning/staleness_detector.py +303 -0
  35. civilization/versioning/version_manager.py +293 -0
  36. cli/__init__.py +1 -0
  37. cli/doctor.py +334 -0
  38. cli/hyperclaw.py +1357 -0
  39. cli/onboarding.py +469 -0
  40. core/__init__.py +1 -0
  41. core/connectors/__init__.py +119 -0
  42. core/connectors/infrastructure.py +772 -0
  43. core/connectors/messaging.py +586 -0
  44. core/connectors/productivity.py +764 -0
  45. core/connectors/smarthome.py +528 -0
  46. core/connectors/social.py +730 -0
  47. core/hyperrouter/__init__.py +23 -0
  48. core/hyperrouter/bandit.py +218 -0
  49. core/hyperrouter/fast_loop.py +68 -0
  50. core/hyperrouter/slow_loop.py +143 -0
  51. core/hyperstate/__init__.py +24 -0
  52. core/hyperstate/certifier.py +154 -0
  53. core/hyperstate/schema.py +160 -0
  54. core/hyperstate/state_manager.py +103 -0
  55. core/hyperstate/store.py +163 -0
  56. core/providers/__init__.py +121 -0
  57. core/providers/embeddings.py +389 -0
  58. core/providers/image.py +534 -0
  59. core/providers/llm.py +1935 -0
  60. core/providers/search.py +608 -0
  61. core/providers/voice.py +474 -0
  62. hyperagents-1.2.3rc5.post3.dist-info/METADATA +469 -0
  63. hyperagents-1.2.3rc5.post3.dist-info/RECORD +361 -0
  64. hyperagents-1.2.3rc5.post3.dist-info/WHEEL +5 -0
  65. hyperagents-1.2.3rc5.post3.dist-info/entry_points.txt +6 -0
  66. hyperagents-1.2.3rc5.post3.dist-info/licenses/LICENSE +21 -0
  67. hyperagents-1.2.3rc5.post3.dist-info/top_level.txt +13 -0
  68. hyperclaw/__init__.py +67 -0
  69. hyperclaw/__main__.py +220 -0
  70. hyperclaw/agent.py +775 -0
  71. hyperclaw/agent_coordinator.py +577 -0
  72. hyperclaw/anthropic_params.py +225 -0
  73. hyperclaw/api_utils.py +85 -0
  74. hyperclaw/approval_telegram.py +209 -0
  75. hyperclaw/approval_terminal.py +80 -0
  76. hyperclaw/attachments.py +177 -0
  77. hyperclaw/branding.py +55 -0
  78. hyperclaw/brief/__init__.py +11 -0
  79. hyperclaw/brief/cli.py +204 -0
  80. hyperclaw/brief/collect.py +146 -0
  81. hyperclaw/brief/compose.py +333 -0
  82. hyperclaw/brief/diff.py +110 -0
  83. hyperclaw/brief/errors.py +99 -0
  84. hyperclaw/brief/google.py +269 -0
  85. hyperclaw/brief/google_auth.py +294 -0
  86. hyperclaw/brief/links.py +124 -0
  87. hyperclaw/brief/service.py +424 -0
  88. hyperclaw/brief/store.py +205 -0
  89. hyperclaw/brief/tasks.py +200 -0
  90. hyperclaw/chat_scope.py +115 -0
  91. hyperclaw/cli_tui.py +497 -0
  92. hyperclaw/client_guard.py +717 -0
  93. hyperclaw/connection_check.py +113 -0
  94. hyperclaw/cost_guard.py +1728 -0
  95. hyperclaw/default_config/agents.yaml +534 -0
  96. hyperclaw/default_config/hyperclaw.yaml +27 -0
  97. hyperclaw/default_config/models.yaml +161 -0
  98. hyperclaw/deployment.py +39 -0
  99. hyperclaw/digest/__init__.py +9 -0
  100. hyperclaw/digest/cli.py +117 -0
  101. hyperclaw/digest/errors.py +100 -0
  102. hyperclaw/digest/gitlab.py +187 -0
  103. hyperclaw/digest/http.py +113 -0
  104. hyperclaw/digest/redact.py +32 -0
  105. hyperclaw/digest/service.py +271 -0
  106. hyperclaw/digest/slack.py +223 -0
  107. hyperclaw/digest/state.py +124 -0
  108. hyperclaw/digest/summarize.py +198 -0
  109. hyperclaw/digest/tokens.py +143 -0
  110. hyperclaw/distribution.py +46 -0
  111. hyperclaw/documents.py +351 -0
  112. hyperclaw/feeds.py +339 -0
  113. hyperclaw/google_workspace/__init__.py +5 -0
  114. hyperclaw/google_workspace/api.py +151 -0
  115. hyperclaw/google_workspace/auth.py +397 -0
  116. hyperclaw/google_workspace/calendar.py +157 -0
  117. hyperclaw/google_workspace/cli.py +72 -0
  118. hyperclaw/google_workspace/drive.py +236 -0
  119. hyperclaw/google_workspace/errors.py +58 -0
  120. hyperclaw/google_workspace/gmail.py +295 -0
  121. hyperclaw/google_workspace/meet.py +122 -0
  122. hyperclaw/google_workspace/tools.py +319 -0
  123. hyperclaw/hyperspeed.py +57 -0
  124. hyperclaw/integrations_layer.py +1427 -0
  125. hyperclaw/live_state.py +188 -0
  126. hyperclaw/managed_config.py +245 -0
  127. hyperclaw/media_hub.py +179 -0
  128. hyperclaw/memory_manager.py +584 -0
  129. hyperclaw/model_catalog.py +551 -0
  130. hyperclaw/model_policy.py +211 -0
  131. hyperclaw/model_router.py +595 -0
  132. hyperclaw/model_selector.py +72 -0
  133. hyperclaw/naming.py +284 -0
  134. hyperclaw/notices.py +65 -0
  135. hyperclaw/oai_stream.py +106 -0
  136. hyperclaw/orchestrator.py +591 -0
  137. hyperclaw/outbox.py +82 -0
  138. hyperclaw/persona.py +367 -0
  139. hyperclaw/prometheus.py +219 -0
  140. hyperclaw/prompt_assembly.py +157 -0
  141. hyperclaw/provider_setup.py +146 -0
  142. hyperclaw/providers.py +340 -0
  143. hyperclaw/qualification.py +341 -0
  144. hyperclaw/qualification_keys/hyper-d292fd127cef5a3e.pub +1 -0
  145. hyperclaw/qualification_records/anthropic--claude-sonnet-5-5.json +14 -0
  146. hyperclaw/reasoning_display.py +35 -0
  147. hyperclaw/scheduler.py +454 -0
  148. hyperclaw/server.py +847 -0
  149. hyperclaw/setup.py +627 -0
  150. hyperclaw/skills.py +717 -0
  151. hyperclaw/solomon.py +442 -0
  152. hyperclaw/swarm.py +533 -0
  153. hyperclaw/taint.py +832 -0
  154. hyperclaw/team/__init__.py +28 -0
  155. hyperclaw/team/dispatcher.py +431 -0
  156. hyperclaw/team/genesis.py +106 -0
  157. hyperclaw/team/lead.py +110 -0
  158. hyperclaw/team/model.py +135 -0
  159. hyperclaw/team/morning.py +238 -0
  160. hyperclaw/team/repos.py +66 -0
  161. hyperclaw/team/roster.py +264 -0
  162. hyperclaw/team/specialist.py +235 -0
  163. hyperclaw/team/status.py +163 -0
  164. hyperclaw/team/watchers.py +112 -0
  165. hyperclaw/teamsetup/__init__.py +17 -0
  166. hyperclaw/teamsetup/checks.py +208 -0
  167. hyperclaw/teamsetup/item.py +391 -0
  168. hyperclaw/teamsetup/oauth_loopback.py +84 -0
  169. hyperclaw/teamsetup/onboard.py +550 -0
  170. hyperclaw/teamsetup/profile.py +82 -0
  171. hyperclaw/teamsetup/secrets.py +313 -0
  172. hyperclaw/teamsetup/selfcheck.py +295 -0
  173. hyperclaw/teamsetup/service.py +495 -0
  174. hyperclaw/teamsetup/slack_oauth.py +442 -0
  175. hyperclaw/teamsetup/tty.py +62 -0
  176. hyperclaw/telegram_bot.py +1106 -0
  177. hyperclaw/telegram_intake.py +126 -0
  178. hyperclaw/telegram_pairing.py +360 -0
  179. hyperclaw/terminal_provider.py +323 -0
  180. hyperclaw/tools/__init__.py +1 -0
  181. hyperclaw/tools/approval.py +949 -0
  182. hyperclaw/tools/args.py +212 -0
  183. hyperclaw/tools/catalog.py +244 -0
  184. hyperclaw/tools/classify.py +266 -0
  185. hyperclaw/tools/effects.py +724 -0
  186. hyperclaw/tools/egress.py +210 -0
  187. hyperclaw/tools/envelope.py +188 -0
  188. hyperclaw/tools/guard.py +367 -0
  189. hyperclaw/tools/ledger.py +339 -0
  190. hyperclaw/tools/receipts.py +70 -0
  191. hyperclaw/tools/registry.py +318 -0
  192. hyperclaw/tools/service_guard.py +151 -0
  193. hyperclaw/tools/surfaces.py +54 -0
  194. hyperclaw/tools/workspace.py +87 -0
  195. hyperclaw/trading/__init__.py +77 -0
  196. hyperclaw/truth_guard.py +791 -0
  197. hyperclaw/tts.py +124 -0
  198. hyperclaw/tui.py +4255 -0
  199. hyperclaw/tui_bridge.py +938 -0
  200. hyperclaw/unattended.py +60 -0
  201. hyperclaw/updates/__init__.py +54 -0
  202. hyperclaw/updates/check.py +131 -0
  203. hyperclaw/updates/index.py +178 -0
  204. hyperclaw/updates/install.py +110 -0
  205. hyperclaw/updates/managed.py +161 -0
  206. hyperclaw/updates/manifest.py +138 -0
  207. hyperclaw/updates/notices.py +86 -0
  208. hyperclaw/updates/paths.py +53 -0
  209. hyperclaw/updates/release.py +146 -0
  210. hyperclaw/updates/upgrade.py +253 -0
  211. hyperclaw/updates/versioning.py +104 -0
  212. hyperclaw/userconfig.py +119 -0
  213. hyperclaw/xai.py +83 -0
  214. integrations/__init__.py +31 -0
  215. integrations/automation/__init__.py +1 -0
  216. integrations/automation/make.py +87 -0
  217. integrations/automation/zapier.py +87 -0
  218. integrations/base.py +476 -0
  219. integrations/communication/__init__.py +1 -0
  220. integrations/communication/twilio.py +279 -0
  221. integrations/communication/zoom.py +290 -0
  222. integrations/data/__init__.py +1 -0
  223. integrations/data/google_analytics.py +135 -0
  224. integrations/data/postgres.py +182 -0
  225. integrations/data/supabase_connector.py +202 -0
  226. integrations/data/weather_feeds.py +450 -0
  227. integrations/developer/__init__.py +9 -0
  228. integrations/developer/github.py +406 -0
  229. integrations/developer/gitlab.py +298 -0
  230. integrations/enterprise/__init__.py +13 -0
  231. integrations/enterprise/confluence.py +266 -0
  232. integrations/enterprise/hubspot.py +293 -0
  233. integrations/enterprise/jira.py +314 -0
  234. integrations/enterprise/salesforce.py +286 -0
  235. integrations/finance/__init__.py +1 -0
  236. integrations/finance/polymarket_client.py +247 -0
  237. integrations/finance/quickbooks.py +281 -0
  238. integrations/finance/stripe.py +261 -0
  239. integrations/gateway.py +238 -0
  240. integrations/messaging/__init__.py +32 -0
  241. integrations/messaging/discord.py +159 -0
  242. integrations/messaging/email.py +401 -0
  243. integrations/messaging/imessage.py +443 -0
  244. integrations/messaging/signal.py +175 -0
  245. integrations/messaging/slack.py +217 -0
  246. integrations/messaging/sms.py +175 -0
  247. integrations/messaging/teams.py +208 -0
  248. integrations/messaging/telegram.py +260 -0
  249. integrations/messaging/whatsapp.py +203 -0
  250. integrations/productivity/__init__.py +17 -0
  251. integrations/productivity/airtable.py +217 -0
  252. integrations/productivity/asana.py +248 -0
  253. integrations/productivity/google/__init__.py +19 -0
  254. integrations/productivity/google/base.py +89 -0
  255. integrations/productivity/google/calendar.py +272 -0
  256. integrations/productivity/google/docs.py +202 -0
  257. integrations/productivity/google/drive.py +268 -0
  258. integrations/productivity/google/gmail.py +279 -0
  259. integrations/productivity/google/meet.py +218 -0
  260. integrations/productivity/google/sheets.py +222 -0
  261. integrations/productivity/google/tasks.py +199 -0
  262. integrations/productivity/linear.py +279 -0
  263. integrations/productivity/microsoft/__init__.py +13 -0
  264. integrations/productivity/microsoft/base.py +93 -0
  265. integrations/productivity/microsoft/calendar.py +207 -0
  266. integrations/productivity/microsoft/onedrive.py +222 -0
  267. integrations/productivity/microsoft/outlook.py +241 -0
  268. integrations/productivity/microsoft/sharepoint.py +211 -0
  269. integrations/productivity/notion.py +243 -0
  270. integrations/productivity/todoist.py +250 -0
  271. integrations/productivity/trello.py +244 -0
  272. integrations/storage/__init__.py +1 -0
  273. integrations/storage/box.py +279 -0
  274. integrations/storage/dropbox.py +215 -0
  275. integrations/telegram_inbound.py +179 -0
  276. integrations/telegram_poller.py +151 -0
  277. memory/__init__.py +1 -0
  278. memory/causal_graph.py +125 -0
  279. memory/impact_tracker.py +88 -0
  280. memory/vector_memory.py +436 -0
  281. models/__init__.py +12 -0
  282. models/chatjimmy_client.py +210 -0
  283. models/claude_client.py +235 -0
  284. models/claude_code_subagent.py +192 -0
  285. models/openai_client.py +120 -0
  286. models/router.py +162 -0
  287. recursive/discovery_loop.py +118 -0
  288. recursive/optimization_loop.py +135 -0
  289. recursive/validation_loop.py +38 -0
  290. security/__init__.py +1 -0
  291. security/audit_logger.py +106 -0
  292. security/filesystem_guard.py +100 -0
  293. security/hypershield.py +99 -0
  294. security/network_guard.py +97 -0
  295. security/policy_engine.py +198 -0
  296. swarm/__init__.py +1 -0
  297. swarm/agents/__init__.py +0 -0
  298. swarm/agents/base.py +230 -0
  299. swarm/agents/business/__init__.py +0 -0
  300. swarm/agents/business/counsel.py +31 -0
  301. swarm/agents/business/herald.py +32 -0
  302. swarm/agents/business/ledger.py +26 -0
  303. swarm/agents/business/nexus.py +30 -0
  304. swarm/agents/business/ops.py +30 -0
  305. swarm/agents/business/pipeline.py +28 -0
  306. swarm/agents/business/revenue.py +30 -0
  307. swarm/agents/business/sovereign.py +31 -0
  308. swarm/agents/business/strategos.py +27 -0
  309. swarm/agents/business/talent.py +27 -0
  310. swarm/agents/business/venture.py +32 -0
  311. swarm/agents/comms/__init__.py +1 -0
  312. swarm/agents/comms/cipher.py +32 -0
  313. swarm/agents/comms/echo_comms.py +31 -0
  314. swarm/agents/comms/envoy.py +31 -0
  315. swarm/agents/comms/herald_scheduler.py +33 -0
  316. swarm/agents/comms/pulse.py +29 -0
  317. swarm/agents/creative/__init__.py +0 -0
  318. swarm/agents/creative/author.py +27 -0
  319. swarm/agents/creative/lens.py +25 -0
  320. swarm/agents/executive/__init__.py +1 -0
  321. swarm/agents/intelligence/__init__.py +1 -0
  322. swarm/agents/intelligence/arbiter.py +30 -0
  323. swarm/agents/intelligence/sentinel.py +30 -0
  324. swarm/agents/media/__init__.py +0 -0
  325. swarm/agents/personal/__init__.py +0 -0
  326. swarm/agents/personal/atlas.py +27 -0
  327. swarm/agents/personal/hearth.py +26 -0
  328. swarm/agents/personal/midas.py +27 -0
  329. swarm/agents/personal/navigator.py +27 -0
  330. swarm/agents/personal/nourish.py +31 -0
  331. swarm/agents/personal/vitals.py +33 -0
  332. swarm/agents/recursive/__init__.py +0 -0
  333. swarm/agents/recursive/alchemist.py +73 -0
  334. swarm/agents/recursive/calibrator.py +45 -0
  335. swarm/agents/recursive/scout.py +97 -0
  336. swarm/agents/scientific/__init__.py +0 -0
  337. swarm/agents/scientific/cosmos.py +36 -0
  338. swarm/agents/scientific/gaia.py +26 -0
  339. swarm/agents/scientific/medicus.py +33 -0
  340. swarm/agents/scientific/oracle_agent.py +39 -0
  341. swarm/agents/scientific/scribe.py +39 -0
  342. swarm/agents/talent/__init__.py +0 -0
  343. swarm/agents/talent/deal.py +28 -0
  344. swarm/agents/talent/roster.py +29 -0
  345. swarm/agents/talent/scout.py +29 -0
  346. swarm/agents/talent/stage.py +27 -0
  347. swarm/agents/tech/__init__.py +0 -0
  348. swarm/agents/tech/aegis.py +28 -0
  349. swarm/agents/tech/bridge.py +28 -0
  350. swarm/agents/tech/forge.py +28 -0
  351. swarm/agents/trading/__init__.py +1 -0
  352. swarm/agents/trading/global_prediction_engine.py +611 -0
  353. swarm/agents/trading/polymarket_trader.py +165 -0
  354. swarm/agents/trading/prediction_strategist.py +218 -0
  355. swarm/autogen_bridge.py +68 -0
  356. swarm/bid_protocol.py +196 -0
  357. swarm/dispatcher.py +527 -0
  358. swarm/nexus.py +181 -0
  359. swarm/registry.py +112 -0
  360. telegram_listener.py +403 -0
  361. ui/tui/app.py +214 -0
@@ -0,0 +1,577 @@
1
+ """
2
+ HyperClaw Agent Coordinator
3
+ Orchestrates all agents, routes tasks by domain, and manages execution.
4
+ """
5
+
6
+ import asyncio
7
+ import logging
8
+ import os
9
+ import re
10
+ import uuid
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime
13
+ from enum import Enum
14
+ from pathlib import Path
15
+ from typing import Any, Callable, Optional
16
+
17
+ import yaml
18
+
19
+ from .model_router import ModelRouter, ModelTier, get_model_router
20
+
21
+ logger = logging.getLogger("hyperclaw.coordinator")
22
+
23
+ # Paths
24
+ ROOT_DIR = Path(__file__).parent.parent
25
+ CONFIG_DIR = ROOT_DIR / "config"
26
+
27
+
28
+ class TaskStatus(Enum):
29
+ PENDING = "pending"
30
+ RUNNING = "running"
31
+ COMPLETED = "completed"
32
+ FAILED = "failed"
33
+
34
+
35
+ @dataclass
36
+ class AgentConfig:
37
+ """Configuration for an agent."""
38
+ id: str
39
+ name: str
40
+ domain: str
41
+ role: str
42
+ tier: int = 1
43
+ model_preference: str = "claude-sonnet"
44
+ task_types: list[str] = field(default_factory=list)
45
+ capabilities: list[str] = field(default_factory=list)
46
+ persona: str = ""
47
+
48
+
49
+ @dataclass
50
+ class Task:
51
+ """A task to be executed by an agent."""
52
+ id: str
53
+ goal: str
54
+ domain: str
55
+ task_type: str = "general"
56
+ priority: int = 5
57
+ assigned_to: Optional[str] = None
58
+ status: TaskStatus = TaskStatus.PENDING
59
+ result: Optional[str] = None
60
+ error: Optional[str] = None
61
+ parent_id: Optional[str] = None
62
+ metadata: dict = field(default_factory=dict)
63
+ created_at: datetime = field(default_factory=datetime.now)
64
+ completed_at: Optional[datetime] = None
65
+
66
+
67
+ # Domain keywords for routing
68
+ DOMAIN_KEYWORDS = {
69
+ "personal": ["schedule", "calendar", "reminder", "task", "todo", "health", "fitness", "meal", "travel", "home"],
70
+ "business": ["meeting", "email", "report", "strategy", "revenue", "sales", "client", "contract", "deal", "pipeline"],
71
+ "communications": ["message", "telegram", "slack", "email", "notification", "alert", "send", "reply"],
72
+ "scientific": ["research", "data", "analysis", "paper", "study", "experiment", "hypothesis"],
73
+ "creative": ["write", "design", "content", "story", "article", "blog", "video", "image"],
74
+ "technology": ["code", "debug", "deploy", "server", "api", "database", "security", "infrastructure"],
75
+ "talent": ["athlete", "talent", "roster", "brand", "sponsorship", "partnership"],
76
+ }
77
+
78
+ # Task type patterns
79
+ TASK_TYPE_PATTERNS = {
80
+ "research": [r"research", r"find out", r"look up", r"investigate", r"what is", r"who is", r"learn about"],
81
+ "analysis": [r"analyze", r"evaluate", r"assess", r"compare", r"review"],
82
+ "synthesis": [r"summarize", r"combine", r"synthesize", r"consolidate", r"compile"],
83
+ "planning": [r"plan", r"strategy", r"schedule", r"organize", r"prioritize"],
84
+ "execution": [r"send", r"create", r"make", r"do", r"execute", r"perform", r"complete"],
85
+ "monitoring": [r"check", r"monitor", r"watch", r"track", r"status"],
86
+ "communication": [r"tell", r"inform", r"notify", r"message", r"email", r"reply"],
87
+ }
88
+
89
+
90
+ class AgentCoordinator:
91
+ """
92
+ Central coordinator for all HyperClaw agents.
93
+ Routes tasks to appropriate agents based on domain and capabilities.
94
+ """
95
+
96
+ def __init__(self, model_router: ModelRouter = None):
97
+ self.model_router = model_router or get_model_router()
98
+ self.agents: dict[str, AgentConfig] = {}
99
+ self.tasks: dict[str, Task] = {}
100
+ self.task_queue: asyncio.Queue = asyncio.Queue()
101
+ self._workers: list[asyncio.Task] = []
102
+ self._running = False
103
+ self._load_agents()
104
+
105
+ def _load_agents(self):
106
+ """Load agent configurations from YAML."""
107
+ from hyperclaw.api_utils import find_config
108
+ agents_file = find_config("agents.yaml")
109
+
110
+ if not agents_file.exists():
111
+ logger.warning(f"Agents config not found: {agents_file}")
112
+ self._create_default_agents()
113
+ return
114
+
115
+ try:
116
+ with open(agents_file) as f:
117
+ config = yaml.safe_load(f)
118
+
119
+ for agent_data in config.get("agents", []):
120
+ agent = AgentConfig(
121
+ id=agent_data.get("id", "unknown"),
122
+ name=agent_data.get("name", agent_data.get("id", "Unknown")),
123
+ domain=agent_data.get("domain", "general"),
124
+ role=agent_data.get("role", ""),
125
+ tier=agent_data.get("tier", 1),
126
+ model_preference=agent_data.get("model_preference", "claude-sonnet"),
127
+ task_types=agent_data.get("task_types", []),
128
+ capabilities=agent_data.get("capabilities", []),
129
+ persona=agent_data.get("persona", ""),
130
+ )
131
+ self.agents[agent.id] = agent
132
+
133
+ logger.info(f"Loaded {len(self.agents)} agents from config")
134
+
135
+ except Exception as e:
136
+ logger.error(f"Failed to load agents config: {e}")
137
+ self._create_default_agents()
138
+
139
+ def _create_default_agents(self):
140
+ """Create default agents if config not available."""
141
+ defaults = [
142
+ AgentConfig("ATLAS", "Atlas", "personal", "Personal productivity assistant", task_types=["planning", "execution"]),
143
+ AgentConfig("HERALD", "Herald", "communications", "Communications manager", task_types=["communication"]),
144
+ AgentConfig("STRATEGOS", "Strategos", "business", "Business strategist", task_types=["analysis", "planning"]),
145
+ AgentConfig("SCRIBE", "Scribe", "scientific", "Research assistant", task_types=["research", "synthesis"]),
146
+ AgentConfig("AUTHOR", "Author", "creative", "Creative writer", task_types=["synthesis", "execution"]),
147
+ AgentConfig("FORGE", "Forge", "technology", "Technical specialist", task_types=["execution", "analysis"]),
148
+ AgentConfig("NEXUS", "Nexus", "executive", "Executive coordinator", task_types=["planning", "monitoring"], tier=0),
149
+ ]
150
+
151
+ for agent in defaults:
152
+ self.agents[agent.id] = agent
153
+
154
+ logger.info(f"Created {len(self.agents)} default agents")
155
+
156
+ # =========================================================================
157
+ # TASK ROUTING
158
+ # =========================================================================
159
+
160
+ def classify_domain(self, message: str) -> str:
161
+ """Classify message into a domain."""
162
+ message_lower = message.lower()
163
+
164
+ domain_scores = {}
165
+ for domain, keywords in DOMAIN_KEYWORDS.items():
166
+ score = sum(1 for kw in keywords if kw in message_lower)
167
+ if score > 0:
168
+ domain_scores[domain] = score
169
+
170
+ if domain_scores:
171
+ return max(domain_scores, key=domain_scores.get)
172
+
173
+ return "general"
174
+
175
+ def classify_task_type(self, message: str) -> str:
176
+ """Classify message into a task type."""
177
+ message_lower = message.lower()
178
+
179
+ for task_type, patterns in TASK_TYPE_PATTERNS.items():
180
+ for pattern in patterns:
181
+ if re.search(pattern, message_lower):
182
+ return task_type
183
+
184
+ return "general"
185
+
186
+ def select_agent(self, task: Task) -> Optional[AgentConfig]:
187
+ """Select the best agent for a task."""
188
+ candidates = []
189
+
190
+ for agent_id, agent in self.agents.items():
191
+ score = 0
192
+
193
+ # Domain match
194
+ if agent.domain == task.domain:
195
+ score += 10
196
+ elif agent.domain == "executive":
197
+ score += 3 # Executive agents can handle anything
198
+
199
+ # Task type match
200
+ if task.task_type in agent.task_types:
201
+ score += 5
202
+
203
+ # Tier bonus (lower tier = more capable)
204
+ score += (3 - agent.tier)
205
+
206
+ if score > 0:
207
+ candidates.append((agent, score))
208
+
209
+ if not candidates:
210
+ # Fallback to NEXUS or first available
211
+ if "NEXUS" in self.agents:
212
+ return self.agents["NEXUS"]
213
+ return list(self.agents.values())[0] if self.agents else None
214
+
215
+ # Sort by score, return best
216
+ candidates.sort(key=lambda x: x[1], reverse=True)
217
+ return candidates[0][0]
218
+
219
+ def determine_model_tier(self, task: Task, agent: AgentConfig) -> ModelTier:
220
+ """Determine which model tier to use for a task."""
221
+ # Simple tasks use fast model
222
+ if task.task_type in ["monitoring", "communication"]:
223
+ return ModelTier.FAST
224
+
225
+ # Complex tasks use standard/premium
226
+ if task.task_type in ["analysis", "research", "planning"]:
227
+ if len(task.goal) > 500 or task.priority >= 8:
228
+ return ModelTier.PREMIUM
229
+ return ModelTier.STANDARD
230
+
231
+ # Execution tasks depend on complexity
232
+ if len(task.goal) < 100:
233
+ return ModelTier.FAST
234
+
235
+ return ModelTier.STANDARD
236
+
237
+ # =========================================================================
238
+ # TASK EXECUTION
239
+ # =========================================================================
240
+
241
+ async def submit_task(
242
+ self,
243
+ goal: str,
244
+ domain: str = None,
245
+ task_type: str = None,
246
+ priority: int = 5,
247
+ agent_id: str = None,
248
+ metadata: dict = None
249
+ ) -> Task:
250
+ """Submit a new task for execution."""
251
+ task_id = str(uuid.uuid4())[:8]
252
+
253
+ # Auto-classify if not provided
254
+ if not domain:
255
+ domain = self.classify_domain(goal)
256
+ if not task_type:
257
+ task_type = self.classify_task_type(goal)
258
+
259
+ task = Task(
260
+ id=task_id,
261
+ goal=goal,
262
+ domain=domain,
263
+ task_type=task_type,
264
+ priority=priority,
265
+ metadata=metadata or {},
266
+ )
267
+
268
+ # Select agent
269
+ if agent_id and agent_id in self.agents:
270
+ task.assigned_to = agent_id
271
+ else:
272
+ agent = self.select_agent(task)
273
+ if agent:
274
+ task.assigned_to = agent.id
275
+
276
+ self.tasks[task_id] = task
277
+ await self.task_queue.put(task)
278
+
279
+ logger.info(f"Task {task_id} submitted: {goal[:50]}... -> {task.assigned_to}")
280
+
281
+ return task
282
+
283
+ async def execute_task(self, task: Task) -> str:
284
+ """Execute a single task."""
285
+ task.status = TaskStatus.RUNNING
286
+
287
+ agent = self.agents.get(task.assigned_to)
288
+ if not agent:
289
+ task.status = TaskStatus.FAILED
290
+ task.error = f"Agent {task.assigned_to} not found"
291
+ return task.error
292
+
293
+ # Determine model tier
294
+ model_tier = self.determine_model_tier(task, agent)
295
+
296
+ # Build agent prompt
297
+ system_prompt = self._build_agent_prompt(agent, task)
298
+
299
+ try:
300
+ # Call model through router
301
+ response, metadata = await self.model_router.call(
302
+ message=task.goal,
303
+ system=system_prompt,
304
+ preferred_tier=model_tier,
305
+ context={"task": task, "agent": agent},
306
+ )
307
+
308
+ task.result = response
309
+ task.status = TaskStatus.COMPLETED
310
+ task.completed_at = datetime.now()
311
+
312
+ logger.info(f"Task {task.id} completed by {agent.id} using {metadata.get('model_name')}")
313
+
314
+ return response
315
+
316
+ except Exception as e:
317
+ task.status = TaskStatus.FAILED
318
+ task.error = str(e)
319
+ logger.error(f"Task {task.id} failed: {e}")
320
+ return f"Error: {e}"
321
+
322
+ def _build_agent_prompt(self, agent: AgentConfig, task: Task) -> str:
323
+ """Build system prompt for an agent."""
324
+ prompt_parts = [
325
+ f"You are {agent.name}, a specialized AI agent.",
326
+ f"Role: {agent.role}",
327
+ f"Domain: {agent.domain}",
328
+ "",
329
+ "## Behavioral Guidelines",
330
+ "- Execute tasks efficiently and completely",
331
+ "- Be concise but thorough",
332
+ "- If you cannot complete a task, explain why clearly",
333
+ "- Provide actionable outputs",
334
+ "",
335
+ ]
336
+
337
+ if agent.persona:
338
+ prompt_parts.append(f"## Persona\n{agent.persona}\n")
339
+
340
+ if task.metadata:
341
+ prompt_parts.append(f"## Task Context\n{task.metadata}\n")
342
+
343
+ prompt_parts.append(f"Current time: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
344
+
345
+ return "\n".join(prompt_parts)
346
+
347
+ # =========================================================================
348
+ # MULTI-AGENT COORDINATION
349
+ # =========================================================================
350
+
351
+ async def coordinate(
352
+ self,
353
+ goal: str,
354
+ context: dict = None,
355
+ max_agents: int = 3
356
+ ) -> dict:
357
+ """
358
+ Coordinate multiple agents to accomplish a complex goal.
359
+ Breaks down the goal and routes sub-tasks to specialists.
360
+ """
361
+ logger.info(f"Coordinating goal: {goal[:100]}...")
362
+
363
+ # First, use NEXUS (or primary agent) to plan
364
+ plan_result = await self._plan_goal(goal, context)
365
+
366
+ # Parse sub-tasks from plan
367
+ sub_tasks = self._parse_subtasks(plan_result)
368
+
369
+ if not sub_tasks:
370
+ # Simple task, execute directly
371
+ task = await self.submit_task(goal)
372
+ result = await self.execute_task(task)
373
+ return {
374
+ "goal": goal,
375
+ "approach": "direct",
376
+ "result": result,
377
+ "tasks": [{"id": task.id, "status": task.status.value}],
378
+ }
379
+
380
+ # Execute sub-tasks (potentially in parallel)
381
+ results = []
382
+ parallel_tasks = []
383
+
384
+ for sub_task_goal in sub_tasks[:max_agents]:
385
+ task = await self.submit_task(sub_task_goal, metadata={"parent_goal": goal})
386
+ parallel_tasks.append(self.execute_task(task))
387
+
388
+ # Wait for all tasks
389
+ task_results = await asyncio.gather(*parallel_tasks, return_exceptions=True)
390
+
391
+ for i, result in enumerate(task_results):
392
+ if isinstance(result, Exception):
393
+ results.append({"error": str(result)})
394
+ else:
395
+ results.append({"result": result})
396
+
397
+ # Synthesize results
398
+ synthesis = await self._synthesize_results(goal, results)
399
+
400
+ return {
401
+ "goal": goal,
402
+ "approach": "coordinated",
403
+ "sub_tasks": len(sub_tasks),
404
+ "results": results,
405
+ "synthesis": synthesis,
406
+ }
407
+
408
+ async def _plan_goal(self, goal: str, context: dict = None) -> str:
409
+ """Use NEXUS to plan how to accomplish a goal."""
410
+ planning_prompt = f"""Analyze this goal and break it into sub-tasks if needed:
411
+
412
+ Goal: {goal}
413
+
414
+ If this is a simple task, respond with: SIMPLE: [brief task description]
415
+
416
+ If this requires multiple steps or specialists, respond with:
417
+ SUBTASKS:
418
+ 1. [First sub-task]
419
+ 2. [Second sub-task]
420
+ ...
421
+
422
+ Be specific and actionable. Maximum 5 sub-tasks."""
423
+
424
+ response, _ = await self.model_router.call(
425
+ message=planning_prompt,
426
+ system="You are a task planning specialist. Break down complex goals into actionable sub-tasks.",
427
+ preferred_tier=ModelTier.FAST, # Planning is fast
428
+ )
429
+
430
+ return response
431
+
432
+ def _parse_subtasks(self, plan_result: str) -> list[str]:
433
+ """Parse sub-tasks from planning result."""
434
+ if "SIMPLE:" in plan_result:
435
+ return []
436
+
437
+ sub_tasks = []
438
+ lines = plan_result.split("\n")
439
+
440
+ for line in lines:
441
+ line = line.strip()
442
+ # Match numbered items
443
+ match = re.match(r"^\d+[\.\)]\s*(.+)$", line)
444
+ if match:
445
+ sub_tasks.append(match.group(1))
446
+ # Match bullet points
447
+ elif line.startswith("- "):
448
+ sub_tasks.append(line[2:])
449
+
450
+ return sub_tasks
451
+
452
+ async def _synthesize_results(self, goal: str, results: list[dict]) -> str:
453
+ """Synthesize results from multiple sub-tasks."""
454
+ results_text = "\n\n".join(
455
+ f"Result {i+1}: {r.get('result', r.get('error', 'No result'))}"
456
+ for i, r in enumerate(results)
457
+ )
458
+
459
+ synthesis_prompt = f"""Original goal: {goal}
460
+
461
+ Sub-task results:
462
+ {results_text}
463
+
464
+ Synthesize these results into a coherent response that addresses the original goal.
465
+ Be concise but complete."""
466
+
467
+ response, _ = await self.model_router.call(
468
+ message=synthesis_prompt,
469
+ system="You synthesize multiple results into coherent responses.",
470
+ preferred_tier=ModelTier.STANDARD,
471
+ )
472
+
473
+ return response
474
+
475
+ # =========================================================================
476
+ # BACKGROUND WORKER
477
+ # =========================================================================
478
+
479
+ async def start_workers(self, num_workers: int = 3):
480
+ """Start background task workers."""
481
+ if self._running:
482
+ return
483
+
484
+ self._running = True
485
+
486
+ for i in range(num_workers):
487
+ worker = asyncio.create_task(self._worker_loop(f"worker-{i}"))
488
+ self._workers.append(worker)
489
+
490
+ logger.info(f"Started {num_workers} task workers")
491
+
492
+ async def stop_workers(self):
493
+ """Stop background workers."""
494
+ self._running = False
495
+
496
+ for worker in self._workers:
497
+ worker.cancel()
498
+
499
+ await asyncio.gather(*self._workers, return_exceptions=True)
500
+ self._workers = []
501
+
502
+ logger.info("Task workers stopped")
503
+
504
+ async def _worker_loop(self, worker_id: str):
505
+ """Worker loop that processes tasks from queue."""
506
+ logger.debug(f"{worker_id} started")
507
+
508
+ while self._running:
509
+ try:
510
+ task = await asyncio.wait_for(self.task_queue.get(), timeout=1.0)
511
+ await self.execute_task(task)
512
+ self.task_queue.task_done()
513
+ except asyncio.TimeoutError:
514
+ continue
515
+ except asyncio.CancelledError:
516
+ break
517
+ except Exception as e:
518
+ logger.error(f"{worker_id} error: {e}")
519
+
520
+ logger.debug(f"{worker_id} stopped")
521
+
522
+ # =========================================================================
523
+ # STATUS & METRICS
524
+ # =========================================================================
525
+
526
+ def get_status(self) -> dict:
527
+ """Get coordinator status."""
528
+ active_tasks = sum(1 for t in self.tasks.values() if t.status == TaskStatus.RUNNING)
529
+ pending_tasks = sum(1 for t in self.tasks.values() if t.status == TaskStatus.PENDING)
530
+ completed_tasks = sum(1 for t in self.tasks.values() if t.status == TaskStatus.COMPLETED)
531
+
532
+ return {
533
+ "agents": len(self.agents),
534
+ "agents_by_domain": self._count_by_domain(),
535
+ "tasks": {
536
+ "total": len(self.tasks),
537
+ "active": active_tasks,
538
+ "pending": pending_tasks,
539
+ "completed": completed_tasks,
540
+ },
541
+ "workers": len(self._workers),
542
+ "running": self._running,
543
+ "model_stats": self.model_router.get_stats(),
544
+ }
545
+
546
+ def _count_by_domain(self) -> dict:
547
+ """Count agents by domain."""
548
+ counts = {}
549
+ for agent in self.agents.values():
550
+ counts[agent.domain] = counts.get(agent.domain, 0) + 1
551
+ return counts
552
+
553
+ def list_agents(self) -> list[dict]:
554
+ """List all agents."""
555
+ return [
556
+ {
557
+ "id": a.id,
558
+ "name": a.name,
559
+ "domain": a.domain,
560
+ "role": a.role,
561
+ "tier": a.tier,
562
+ "task_types": a.task_types,
563
+ }
564
+ for a in self.agents.values()
565
+ ]
566
+
567
+
568
+ # Singleton
569
+ _coordinator: Optional[AgentCoordinator] = None
570
+
571
+
572
+ async def get_coordinator() -> AgentCoordinator:
573
+ """Get or create coordinator singleton."""
574
+ global _coordinator
575
+ if _coordinator is None:
576
+ _coordinator = AgentCoordinator()
577
+ return _coordinator