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,279 @@
1
+ """GmailConnector — Gmail API integration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import logging
7
+ from typing import AsyncIterator
8
+
9
+ import httpx
10
+
11
+ from integrations.base import (
12
+ BaseConnector,
13
+ ConnectorCapability,
14
+ ConnectorInfo,
15
+ InboundMessage,
16
+ OutboundMessage,
17
+ )
18
+ from integrations.productivity.google.base import GoogleOAuthBase
19
+
20
+ logger = logging.getLogger("hyperclaw.integrations.gmail")
21
+
22
+
23
+ class GmailConnector(BaseConnector, GoogleOAuthBase):
24
+ """
25
+ Gmail API connector.
26
+
27
+ Required config (via GoogleOAuthBase):
28
+ - access_token or credentials_file
29
+ """
30
+
31
+ BASE_URL = "https://gmail.googleapis.com/gmail/v1"
32
+
33
+ def __init__(self, config: dict) -> None:
34
+ self.config = config
35
+ self._init_google_auth(config)
36
+ self._validate_google_config(config)
37
+ self._client: httpx.AsyncClient | None = None
38
+
39
+ @property
40
+ def info(self) -> ConnectorInfo:
41
+ return ConnectorInfo(
42
+ connector_id="gmail",
43
+ platform="gmail",
44
+ category="productivity",
45
+ capabilities=frozenset(
46
+ [
47
+ ConnectorCapability.SEND_MESSAGE,
48
+ ConnectorCapability.RECEIVE_MESSAGE,
49
+ ConnectorCapability.READ_DATA,
50
+ ConnectorCapability.WRITE_DATA,
51
+ ConnectorCapability.DELETE_DATA,
52
+ ConnectorCapability.LIST_DATA,
53
+ ConnectorCapability.SEARCH,
54
+ ConnectorCapability.FILE_UPLOAD,
55
+ ConnectorCapability.FILE_DOWNLOAD,
56
+ ]
57
+ ),
58
+ auth_type="oauth2",
59
+ rate_limit_per_minute=250,
60
+ supports_threads=True,
61
+ supports_attachments=True,
62
+ supports_reactions=False,
63
+ )
64
+
65
+ async def _get_client(self) -> httpx.AsyncClient:
66
+ return await self._get_google_client()
67
+
68
+ async def health(self) -> bool:
69
+ """Check if Gmail API is accessible."""
70
+ try:
71
+ client = await self._get_client()
72
+ response = await client.get(f"{self.BASE_URL}/users/me/profile")
73
+ return response.status_code == 200
74
+ except Exception as e:
75
+ logger.error(f"Gmail health check failed: {e}")
76
+ return False
77
+
78
+ async def _send_impl(self, message: OutboundMessage) -> str:
79
+ """Send an email via Gmail."""
80
+ return await self.send_email(
81
+ to=message.recipient_id,
82
+ subject="Message",
83
+ body=message.content,
84
+ )
85
+
86
+ async def _receive_impl(self) -> AsyncIterator[InboundMessage]:
87
+ """Receive emails from inbox."""
88
+ messages = await self.list_inbox(max_results=20)
89
+
90
+ client = await self._get_client()
91
+
92
+ for msg_ref in messages:
93
+ response = await client.get(
94
+ f"{self.BASE_URL}/users/me/messages/{msg_ref['id']}"
95
+ )
96
+
97
+ if response.status_code != 200:
98
+ continue
99
+
100
+ msg = response.json()
101
+ headers = {
102
+ h["name"]: h["value"]
103
+ for h in msg.get("payload", {}).get("headers", [])
104
+ }
105
+
106
+ yield InboundMessage(
107
+ message_id=msg.get("id", ""),
108
+ platform="gmail",
109
+ sender_id=headers.get("From", ""),
110
+ sender_name=headers.get("From", ""),
111
+ content=msg.get("snippet", ""),
112
+ thread_id=msg.get("threadId"),
113
+ raw=msg,
114
+ )
115
+
116
+ async def send_email(
117
+ self,
118
+ to: str,
119
+ subject: str,
120
+ body: str,
121
+ attachments: list[str] | None = None,
122
+ ) -> str:
123
+ """Send an email."""
124
+ client = await self._get_client()
125
+
126
+ # Build raw email
127
+ email_lines = [
128
+ f"To: {to}",
129
+ f"Subject: {subject}",
130
+ "Content-Type: text/plain; charset=utf-8",
131
+ "",
132
+ body,
133
+ ]
134
+ email_content = "\r\n".join(email_lines)
135
+ raw = base64.urlsafe_b64encode(email_content.encode()).decode()
136
+
137
+ response = await client.post(
138
+ f"{self.BASE_URL}/users/me/messages/send",
139
+ json={"raw": raw},
140
+ )
141
+
142
+ if response.status_code not in (200, 201):
143
+ raise Exception(f"Gmail send failed: {response.text}")
144
+
145
+ return response.json().get("id", "")
146
+
147
+ async def list_inbox(
148
+ self,
149
+ max_results: int = 20,
150
+ query: str = "",
151
+ ) -> list[dict]:
152
+ """List inbox messages."""
153
+ client = await self._get_client()
154
+
155
+ params = {"maxResults": max_results, "labelIds": "INBOX"}
156
+ if query:
157
+ params["q"] = query
158
+
159
+ response = await client.get(
160
+ f"{self.BASE_URL}/users/me/messages",
161
+ params=params,
162
+ )
163
+
164
+ if response.status_code != 200:
165
+ raise Exception(f"Gmail list failed: {response.text}")
166
+
167
+ return response.json().get("messages", [])
168
+
169
+ async def search_emails(self, query: str) -> list[dict]:
170
+ """Search emails."""
171
+ return await self.list_inbox(max_results=50, query=query)
172
+
173
+ async def get_thread(self, thread_id: str) -> dict:
174
+ """Get an email thread."""
175
+ client = await self._get_client()
176
+
177
+ response = await client.get(
178
+ f"{self.BASE_URL}/users/me/threads/{thread_id}"
179
+ )
180
+
181
+ if response.status_code != 200:
182
+ raise Exception(f"Gmail get thread failed: {response.text}")
183
+
184
+ return response.json()
185
+
186
+ async def create_draft(
187
+ self,
188
+ to: str,
189
+ subject: str,
190
+ body: str,
191
+ ) -> str:
192
+ """Create an email draft."""
193
+ client = await self._get_client()
194
+
195
+ email_lines = [f"To: {to}", f"Subject: {subject}", "", body]
196
+ email_content = "\r\n".join(email_lines)
197
+ raw = base64.urlsafe_b64encode(email_content.encode()).decode()
198
+
199
+ response = await client.post(
200
+ f"{self.BASE_URL}/users/me/drafts",
201
+ json={"message": {"raw": raw}},
202
+ )
203
+
204
+ if response.status_code not in (200, 201):
205
+ raise Exception(f"Gmail create draft failed: {response.text}")
206
+
207
+ return response.json().get("id", "")
208
+
209
+ async def mark_read(self, message_id: str) -> bool:
210
+ """Mark a message as read."""
211
+ client = await self._get_client()
212
+
213
+ response = await client.post(
214
+ f"{self.BASE_URL}/users/me/messages/{message_id}/modify",
215
+ json={"removeLabelIds": ["UNREAD"]},
216
+ )
217
+
218
+ return response.status_code == 200
219
+
220
+ async def add_label(self, message_id: str, label: str) -> bool:
221
+ """Add a label to a message."""
222
+ client = await self._get_client()
223
+
224
+ response = await client.post(
225
+ f"{self.BASE_URL}/users/me/messages/{message_id}/modify",
226
+ json={"addLabelIds": [label]},
227
+ )
228
+
229
+ return response.status_code == 200
230
+
231
+ async def create_filter(self, criteria: dict, action: dict) -> str:
232
+ """Create an email filter."""
233
+ client = await self._get_client()
234
+
235
+ response = await client.post(
236
+ f"{self.BASE_URL}/users/me/settings/filters",
237
+ json={"criteria": criteria, "action": action},
238
+ )
239
+
240
+ if response.status_code not in (200, 201):
241
+ raise Exception(f"Gmail create filter failed: {response.text}")
242
+
243
+ return response.json().get("id", "")
244
+
245
+ async def _read_impl(self, resource_id: str, **kwargs) -> dict:
246
+ """Read a message."""
247
+ client = await self._get_client()
248
+
249
+ response = await client.get(
250
+ f"{self.BASE_URL}/users/me/messages/{resource_id}"
251
+ )
252
+
253
+ if response.status_code != 200:
254
+ raise Exception(f"Gmail read failed: {response.text}")
255
+
256
+ return response.json()
257
+
258
+ async def _delete_impl(self, resource_id: str, **kwargs) -> bool:
259
+ """Delete a message."""
260
+ client = await self._get_client()
261
+
262
+ response = await client.delete(
263
+ f"{self.BASE_URL}/users/me/messages/{resource_id}"
264
+ )
265
+
266
+ return response.status_code == 204
267
+
268
+ async def _search_impl(self, query: str, **kwargs) -> list[dict]:
269
+ """Search implementation."""
270
+ return await self.search_emails(query)
271
+
272
+ async def _list_impl(
273
+ self, resource_type: str, filters: dict, **kwargs
274
+ ) -> list[dict]:
275
+ """List implementation."""
276
+ return await self.list_inbox(
277
+ max_results=filters.get("max_results", 20),
278
+ query=filters.get("query", ""),
279
+ )
@@ -0,0 +1,218 @@
1
+ """GoogleMeetConnector — Google Meet API integration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from datetime import datetime
7
+
8
+ import httpx
9
+
10
+ from integrations.base import (
11
+ BaseConnector,
12
+ ConnectorCapability,
13
+ ConnectorInfo,
14
+ )
15
+ from integrations.productivity.google.base import GoogleOAuthBase
16
+
17
+ logger = logging.getLogger("hyperclaw.integrations.google_meet")
18
+
19
+
20
+ class GoogleMeetConnector(BaseConnector, GoogleOAuthBase):
21
+ """
22
+ Google Meet API connector.
23
+
24
+ Uses Google Calendar API to create Meet events.
25
+
26
+ Required config (via GoogleOAuthBase):
27
+ - access_token or credentials_file
28
+ """
29
+
30
+ CALENDAR_URL = "https://www.googleapis.com/calendar/v3"
31
+
32
+ def __init__(self, config: dict) -> None:
33
+ self.config = config
34
+ self._init_google_auth(config)
35
+ self._validate_google_config(config)
36
+
37
+ @property
38
+ def info(self) -> ConnectorInfo:
39
+ return ConnectorInfo(
40
+ connector_id="google_meet",
41
+ platform="google_meet",
42
+ category="productivity",
43
+ capabilities=frozenset(
44
+ [
45
+ ConnectorCapability.READ_DATA,
46
+ ConnectorCapability.WRITE_DATA,
47
+ ConnectorCapability.ACTION,
48
+ ]
49
+ ),
50
+ auth_type="oauth2",
51
+ rate_limit_per_minute=60,
52
+ )
53
+
54
+ async def _get_client(self) -> httpx.AsyncClient:
55
+ return await self._get_google_client()
56
+
57
+ async def health(self) -> bool:
58
+ """Check if Meet/Calendar API is accessible."""
59
+ try:
60
+ client = await self._get_client()
61
+ response = await client.get(
62
+ f"{self.CALENDAR_URL}/users/me/calendarList",
63
+ )
64
+ return response.status_code == 200
65
+ except Exception as e:
66
+ logger.error(f"Google Meet health check failed: {e}")
67
+ return False
68
+
69
+ async def create_meeting(
70
+ self,
71
+ title: str,
72
+ start: datetime,
73
+ end: datetime,
74
+ attendees: list[str] | None = None,
75
+ ) -> dict:
76
+ """Create a Google Meet meeting via Calendar."""
77
+ client = await self._get_client()
78
+
79
+ event = {
80
+ "summary": title,
81
+ "start": {"dateTime": start.isoformat(), "timeZone": "UTC"},
82
+ "end": {"dateTime": end.isoformat(), "timeZone": "UTC"},
83
+ "conferenceData": {
84
+ "createRequest": {
85
+ "requestId": f"meet-{datetime.utcnow().timestamp()}",
86
+ "conferenceSolutionKey": {"type": "hangoutsMeet"},
87
+ }
88
+ },
89
+ }
90
+
91
+ if attendees:
92
+ event["attendees"] = [{"email": email} for email in attendees]
93
+
94
+ response = await client.post(
95
+ f"{self.CALENDAR_URL}/calendars/primary/events",
96
+ params={"conferenceDataVersion": 1},
97
+ json=event,
98
+ )
99
+
100
+ if response.status_code not in (200, 201):
101
+ raise Exception(f"Meet create meeting failed: {response.text}")
102
+
103
+ data = response.json()
104
+
105
+ # Extract Meet link
106
+ conference_data = data.get("conferenceData", {})
107
+ entry_points = conference_data.get("entryPoints", [])
108
+ meet_link = next(
109
+ (ep.get("uri") for ep in entry_points if ep.get("entryPointType") == "video"),
110
+ None,
111
+ )
112
+
113
+ return {
114
+ "event_id": data.get("id"),
115
+ "meet_link": meet_link,
116
+ "calendar_link": data.get("htmlLink"),
117
+ "start": data.get("start"),
118
+ "end": data.get("end"),
119
+ }
120
+
121
+ async def get_meeting_transcript(self, meeting_id: str) -> dict:
122
+ """
123
+ Get meeting transcript.
124
+ Note: This requires Meet transcription to be enabled and
125
+ uses the Google Cloud Speech-to-Text API or Meet add-ons.
126
+ This is a placeholder for the interface.
127
+ """
128
+ # Meet transcripts are stored in Drive or via external services
129
+ # This would require additional API integration
130
+ return {
131
+ "meeting_id": meeting_id,
132
+ "transcript": None,
133
+ "status": "Transcript retrieval requires additional configuration",
134
+ }
135
+
136
+ async def list_upcoming_meetings(
137
+ self,
138
+ max_results: int = 10,
139
+ ) -> list[dict]:
140
+ """List upcoming meetings with Meet links."""
141
+ client = await self._get_client()
142
+
143
+ now = datetime.utcnow().isoformat() + "Z"
144
+
145
+ response = await client.get(
146
+ f"{self.CALENDAR_URL}/calendars/primary/events",
147
+ params={
148
+ "timeMin": now,
149
+ "maxResults": max_results,
150
+ "singleEvents": True,
151
+ "orderBy": "startTime",
152
+ },
153
+ )
154
+
155
+ if response.status_code != 200:
156
+ raise Exception(f"Meet list meetings failed: {response.text}")
157
+
158
+ events = response.json().get("items", [])
159
+
160
+ # Filter to only events with Meet links
161
+ meetings = []
162
+ for event in events:
163
+ conference_data = event.get("conferenceData", {})
164
+ entry_points = conference_data.get("entryPoints", [])
165
+ meet_link = next(
166
+ (ep.get("uri") for ep in entry_points if ep.get("entryPointType") == "video"),
167
+ None,
168
+ )
169
+
170
+ if meet_link:
171
+ meetings.append({
172
+ "event_id": event.get("id"),
173
+ "title": event.get("summary"),
174
+ "meet_link": meet_link,
175
+ "start": event.get("start"),
176
+ "end": event.get("end"),
177
+ "attendees": event.get("attendees", []),
178
+ })
179
+
180
+ return meetings
181
+
182
+ async def _read_impl(self, resource_id: str, **kwargs) -> dict:
183
+ """Read a meeting event."""
184
+ client = await self._get_client()
185
+
186
+ response = await client.get(
187
+ f"{self.CALENDAR_URL}/calendars/primary/events/{resource_id}"
188
+ )
189
+
190
+ if response.status_code != 200:
191
+ raise Exception(f"Meet read failed: {response.text}")
192
+
193
+ return response.json()
194
+
195
+ async def _write_impl(self, resource_type: str, data: dict, **kwargs) -> dict:
196
+ """Create a meeting."""
197
+ return await self.create_meeting(
198
+ title=data.get("title", "Meeting"),
199
+ start=data.get("start"),
200
+ end=data.get("end"),
201
+ attendees=data.get("attendees"),
202
+ )
203
+
204
+ async def _action_impl(self, action_name: str, params: dict) -> dict:
205
+ """Execute Meet-specific actions."""
206
+ if action_name == "create_meeting":
207
+ return await self.create_meeting(
208
+ title=params.get("title", "Meeting"),
209
+ start=params["start"],
210
+ end=params["end"],
211
+ attendees=params.get("attendees"),
212
+ )
213
+ elif action_name == "get_transcript":
214
+ return await self.get_meeting_transcript(params["meeting_id"])
215
+ elif action_name == "list_upcoming":
216
+ return {"meetings": await self.list_upcoming_meetings(params.get("max_results", 10))}
217
+
218
+ raise ValueError(f"Unknown action: {action_name}")