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.
- agents/__init__.py +31 -0
- agents/background_monitor.py +453 -0
- agents/learning.py +460 -0
- agents/planner.py +421 -0
- agents/transcriber.py +341 -0
- civilization/__init__.py +51 -0
- civilization/api/__init__.py +7 -0
- civilization/api/civilization_api.py +361 -0
- civilization/graph/__init__.py +19 -0
- civilization/graph/knowledge_linker.py +348 -0
- civilization/graph/org_graph.py +348 -0
- civilization/graph/process_graph.py +386 -0
- civilization/ingestion/__init__.py +20 -0
- civilization/ingestion/chunker.py +273 -0
- civilization/ingestion/document_ingestor.py +358 -0
- civilization/ingestion/embedder.py +262 -0
- civilization/ingestion/extractor.py +224 -0
- civilization/interview/__init__.py +19 -0
- civilization/interview/gap_detector.py +380 -0
- civilization/interview/interviewer.py +323 -0
- civilization/interview/questioner.py +363 -0
- civilization/retrieval/__init__.py +17 -0
- civilization/retrieval/civilization_rag.py +316 -0
- civilization/retrieval/context_injector.py +342 -0
- civilization/retrieval/relevance_ranker.py +287 -0
- civilization/schema.py +299 -0
- civilization/store.py +177 -0
- civilization/sync/__init__.py +16 -0
- civilization/sync/confluence_sync.py +317 -0
- civilization/sync/gdrive_sync.py +262 -0
- civilization/sync/notion_sync.py +237 -0
- civilization/versioning/__init__.py +19 -0
- civilization/versioning/diff_engine.py +351 -0
- civilization/versioning/staleness_detector.py +303 -0
- civilization/versioning/version_manager.py +293 -0
- cli/__init__.py +1 -0
- cli/doctor.py +334 -0
- cli/hyperclaw.py +1357 -0
- cli/onboarding.py +469 -0
- core/__init__.py +1 -0
- core/connectors/__init__.py +119 -0
- core/connectors/infrastructure.py +772 -0
- core/connectors/messaging.py +586 -0
- core/connectors/productivity.py +764 -0
- core/connectors/smarthome.py +528 -0
- core/connectors/social.py +730 -0
- core/hyperrouter/__init__.py +23 -0
- core/hyperrouter/bandit.py +218 -0
- core/hyperrouter/fast_loop.py +68 -0
- core/hyperrouter/slow_loop.py +143 -0
- core/hyperstate/__init__.py +24 -0
- core/hyperstate/certifier.py +154 -0
- core/hyperstate/schema.py +160 -0
- core/hyperstate/state_manager.py +103 -0
- core/hyperstate/store.py +163 -0
- core/providers/__init__.py +121 -0
- core/providers/embeddings.py +389 -0
- core/providers/image.py +534 -0
- core/providers/llm.py +1935 -0
- core/providers/search.py +608 -0
- core/providers/voice.py +474 -0
- hyperagents-1.2.3rc5.post3.dist-info/METADATA +469 -0
- hyperagents-1.2.3rc5.post3.dist-info/RECORD +361 -0
- hyperagents-1.2.3rc5.post3.dist-info/WHEEL +5 -0
- hyperagents-1.2.3rc5.post3.dist-info/entry_points.txt +6 -0
- hyperagents-1.2.3rc5.post3.dist-info/licenses/LICENSE +21 -0
- hyperagents-1.2.3rc5.post3.dist-info/top_level.txt +13 -0
- hyperclaw/__init__.py +67 -0
- hyperclaw/__main__.py +220 -0
- hyperclaw/agent.py +775 -0
- hyperclaw/agent_coordinator.py +577 -0
- hyperclaw/anthropic_params.py +225 -0
- hyperclaw/api_utils.py +85 -0
- hyperclaw/approval_telegram.py +209 -0
- hyperclaw/approval_terminal.py +80 -0
- hyperclaw/attachments.py +177 -0
- hyperclaw/branding.py +55 -0
- hyperclaw/brief/__init__.py +11 -0
- hyperclaw/brief/cli.py +204 -0
- hyperclaw/brief/collect.py +146 -0
- hyperclaw/brief/compose.py +333 -0
- hyperclaw/brief/diff.py +110 -0
- hyperclaw/brief/errors.py +99 -0
- hyperclaw/brief/google.py +269 -0
- hyperclaw/brief/google_auth.py +294 -0
- hyperclaw/brief/links.py +124 -0
- hyperclaw/brief/service.py +424 -0
- hyperclaw/brief/store.py +205 -0
- hyperclaw/brief/tasks.py +200 -0
- hyperclaw/chat_scope.py +115 -0
- hyperclaw/cli_tui.py +497 -0
- hyperclaw/client_guard.py +717 -0
- hyperclaw/connection_check.py +113 -0
- hyperclaw/cost_guard.py +1728 -0
- hyperclaw/default_config/agents.yaml +534 -0
- hyperclaw/default_config/hyperclaw.yaml +27 -0
- hyperclaw/default_config/models.yaml +161 -0
- hyperclaw/deployment.py +39 -0
- hyperclaw/digest/__init__.py +9 -0
- hyperclaw/digest/cli.py +117 -0
- hyperclaw/digest/errors.py +100 -0
- hyperclaw/digest/gitlab.py +187 -0
- hyperclaw/digest/http.py +113 -0
- hyperclaw/digest/redact.py +32 -0
- hyperclaw/digest/service.py +271 -0
- hyperclaw/digest/slack.py +223 -0
- hyperclaw/digest/state.py +124 -0
- hyperclaw/digest/summarize.py +198 -0
- hyperclaw/digest/tokens.py +143 -0
- hyperclaw/distribution.py +46 -0
- hyperclaw/documents.py +351 -0
- hyperclaw/feeds.py +339 -0
- hyperclaw/google_workspace/__init__.py +5 -0
- hyperclaw/google_workspace/api.py +151 -0
- hyperclaw/google_workspace/auth.py +397 -0
- hyperclaw/google_workspace/calendar.py +157 -0
- hyperclaw/google_workspace/cli.py +72 -0
- hyperclaw/google_workspace/drive.py +236 -0
- hyperclaw/google_workspace/errors.py +58 -0
- hyperclaw/google_workspace/gmail.py +295 -0
- hyperclaw/google_workspace/meet.py +122 -0
- hyperclaw/google_workspace/tools.py +319 -0
- hyperclaw/hyperspeed.py +57 -0
- hyperclaw/integrations_layer.py +1427 -0
- hyperclaw/live_state.py +188 -0
- hyperclaw/managed_config.py +245 -0
- hyperclaw/media_hub.py +179 -0
- hyperclaw/memory_manager.py +584 -0
- hyperclaw/model_catalog.py +551 -0
- hyperclaw/model_policy.py +211 -0
- hyperclaw/model_router.py +595 -0
- hyperclaw/model_selector.py +72 -0
- hyperclaw/naming.py +284 -0
- hyperclaw/notices.py +65 -0
- hyperclaw/oai_stream.py +106 -0
- hyperclaw/orchestrator.py +591 -0
- hyperclaw/outbox.py +82 -0
- hyperclaw/persona.py +367 -0
- hyperclaw/prometheus.py +219 -0
- hyperclaw/prompt_assembly.py +157 -0
- hyperclaw/provider_setup.py +146 -0
- hyperclaw/providers.py +340 -0
- hyperclaw/qualification.py +341 -0
- hyperclaw/qualification_keys/hyper-d292fd127cef5a3e.pub +1 -0
- hyperclaw/qualification_records/anthropic--claude-sonnet-5-5.json +14 -0
- hyperclaw/reasoning_display.py +35 -0
- hyperclaw/scheduler.py +454 -0
- hyperclaw/server.py +847 -0
- hyperclaw/setup.py +627 -0
- hyperclaw/skills.py +717 -0
- hyperclaw/solomon.py +442 -0
- hyperclaw/swarm.py +533 -0
- hyperclaw/taint.py +832 -0
- hyperclaw/team/__init__.py +28 -0
- hyperclaw/team/dispatcher.py +431 -0
- hyperclaw/team/genesis.py +106 -0
- hyperclaw/team/lead.py +110 -0
- hyperclaw/team/model.py +135 -0
- hyperclaw/team/morning.py +238 -0
- hyperclaw/team/repos.py +66 -0
- hyperclaw/team/roster.py +264 -0
- hyperclaw/team/specialist.py +235 -0
- hyperclaw/team/status.py +163 -0
- hyperclaw/team/watchers.py +112 -0
- hyperclaw/teamsetup/__init__.py +17 -0
- hyperclaw/teamsetup/checks.py +208 -0
- hyperclaw/teamsetup/item.py +391 -0
- hyperclaw/teamsetup/oauth_loopback.py +84 -0
- hyperclaw/teamsetup/onboard.py +550 -0
- hyperclaw/teamsetup/profile.py +82 -0
- hyperclaw/teamsetup/secrets.py +313 -0
- hyperclaw/teamsetup/selfcheck.py +295 -0
- hyperclaw/teamsetup/service.py +495 -0
- hyperclaw/teamsetup/slack_oauth.py +442 -0
- hyperclaw/teamsetup/tty.py +62 -0
- hyperclaw/telegram_bot.py +1106 -0
- hyperclaw/telegram_intake.py +126 -0
- hyperclaw/telegram_pairing.py +360 -0
- hyperclaw/terminal_provider.py +323 -0
- hyperclaw/tools/__init__.py +1 -0
- hyperclaw/tools/approval.py +949 -0
- hyperclaw/tools/args.py +212 -0
- hyperclaw/tools/catalog.py +244 -0
- hyperclaw/tools/classify.py +266 -0
- hyperclaw/tools/effects.py +724 -0
- hyperclaw/tools/egress.py +210 -0
- hyperclaw/tools/envelope.py +188 -0
- hyperclaw/tools/guard.py +367 -0
- hyperclaw/tools/ledger.py +339 -0
- hyperclaw/tools/receipts.py +70 -0
- hyperclaw/tools/registry.py +318 -0
- hyperclaw/tools/service_guard.py +151 -0
- hyperclaw/tools/surfaces.py +54 -0
- hyperclaw/tools/workspace.py +87 -0
- hyperclaw/trading/__init__.py +77 -0
- hyperclaw/truth_guard.py +791 -0
- hyperclaw/tts.py +124 -0
- hyperclaw/tui.py +4255 -0
- hyperclaw/tui_bridge.py +938 -0
- hyperclaw/unattended.py +60 -0
- hyperclaw/updates/__init__.py +54 -0
- hyperclaw/updates/check.py +131 -0
- hyperclaw/updates/index.py +178 -0
- hyperclaw/updates/install.py +110 -0
- hyperclaw/updates/managed.py +161 -0
- hyperclaw/updates/manifest.py +138 -0
- hyperclaw/updates/notices.py +86 -0
- hyperclaw/updates/paths.py +53 -0
- hyperclaw/updates/release.py +146 -0
- hyperclaw/updates/upgrade.py +253 -0
- hyperclaw/updates/versioning.py +104 -0
- hyperclaw/userconfig.py +119 -0
- hyperclaw/xai.py +83 -0
- integrations/__init__.py +31 -0
- integrations/automation/__init__.py +1 -0
- integrations/automation/make.py +87 -0
- integrations/automation/zapier.py +87 -0
- integrations/base.py +476 -0
- integrations/communication/__init__.py +1 -0
- integrations/communication/twilio.py +279 -0
- integrations/communication/zoom.py +290 -0
- integrations/data/__init__.py +1 -0
- integrations/data/google_analytics.py +135 -0
- integrations/data/postgres.py +182 -0
- integrations/data/supabase_connector.py +202 -0
- integrations/data/weather_feeds.py +450 -0
- integrations/developer/__init__.py +9 -0
- integrations/developer/github.py +406 -0
- integrations/developer/gitlab.py +298 -0
- integrations/enterprise/__init__.py +13 -0
- integrations/enterprise/confluence.py +266 -0
- integrations/enterprise/hubspot.py +293 -0
- integrations/enterprise/jira.py +314 -0
- integrations/enterprise/salesforce.py +286 -0
- integrations/finance/__init__.py +1 -0
- integrations/finance/polymarket_client.py +247 -0
- integrations/finance/quickbooks.py +281 -0
- integrations/finance/stripe.py +261 -0
- integrations/gateway.py +238 -0
- integrations/messaging/__init__.py +32 -0
- integrations/messaging/discord.py +159 -0
- integrations/messaging/email.py +401 -0
- integrations/messaging/imessage.py +443 -0
- integrations/messaging/signal.py +175 -0
- integrations/messaging/slack.py +217 -0
- integrations/messaging/sms.py +175 -0
- integrations/messaging/teams.py +208 -0
- integrations/messaging/telegram.py +260 -0
- integrations/messaging/whatsapp.py +203 -0
- integrations/productivity/__init__.py +17 -0
- integrations/productivity/airtable.py +217 -0
- integrations/productivity/asana.py +248 -0
- integrations/productivity/google/__init__.py +19 -0
- integrations/productivity/google/base.py +89 -0
- integrations/productivity/google/calendar.py +272 -0
- integrations/productivity/google/docs.py +202 -0
- integrations/productivity/google/drive.py +268 -0
- integrations/productivity/google/gmail.py +279 -0
- integrations/productivity/google/meet.py +218 -0
- integrations/productivity/google/sheets.py +222 -0
- integrations/productivity/google/tasks.py +199 -0
- integrations/productivity/linear.py +279 -0
- integrations/productivity/microsoft/__init__.py +13 -0
- integrations/productivity/microsoft/base.py +93 -0
- integrations/productivity/microsoft/calendar.py +207 -0
- integrations/productivity/microsoft/onedrive.py +222 -0
- integrations/productivity/microsoft/outlook.py +241 -0
- integrations/productivity/microsoft/sharepoint.py +211 -0
- integrations/productivity/notion.py +243 -0
- integrations/productivity/todoist.py +250 -0
- integrations/productivity/trello.py +244 -0
- integrations/storage/__init__.py +1 -0
- integrations/storage/box.py +279 -0
- integrations/storage/dropbox.py +215 -0
- integrations/telegram_inbound.py +179 -0
- integrations/telegram_poller.py +151 -0
- memory/__init__.py +1 -0
- memory/causal_graph.py +125 -0
- memory/impact_tracker.py +88 -0
- memory/vector_memory.py +436 -0
- models/__init__.py +12 -0
- models/chatjimmy_client.py +210 -0
- models/claude_client.py +235 -0
- models/claude_code_subagent.py +192 -0
- models/openai_client.py +120 -0
- models/router.py +162 -0
- recursive/discovery_loop.py +118 -0
- recursive/optimization_loop.py +135 -0
- recursive/validation_loop.py +38 -0
- security/__init__.py +1 -0
- security/audit_logger.py +106 -0
- security/filesystem_guard.py +100 -0
- security/hypershield.py +99 -0
- security/network_guard.py +97 -0
- security/policy_engine.py +198 -0
- swarm/__init__.py +1 -0
- swarm/agents/__init__.py +0 -0
- swarm/agents/base.py +230 -0
- swarm/agents/business/__init__.py +0 -0
- swarm/agents/business/counsel.py +31 -0
- swarm/agents/business/herald.py +32 -0
- swarm/agents/business/ledger.py +26 -0
- swarm/agents/business/nexus.py +30 -0
- swarm/agents/business/ops.py +30 -0
- swarm/agents/business/pipeline.py +28 -0
- swarm/agents/business/revenue.py +30 -0
- swarm/agents/business/sovereign.py +31 -0
- swarm/agents/business/strategos.py +27 -0
- swarm/agents/business/talent.py +27 -0
- swarm/agents/business/venture.py +32 -0
- swarm/agents/comms/__init__.py +1 -0
- swarm/agents/comms/cipher.py +32 -0
- swarm/agents/comms/echo_comms.py +31 -0
- swarm/agents/comms/envoy.py +31 -0
- swarm/agents/comms/herald_scheduler.py +33 -0
- swarm/agents/comms/pulse.py +29 -0
- swarm/agents/creative/__init__.py +0 -0
- swarm/agents/creative/author.py +27 -0
- swarm/agents/creative/lens.py +25 -0
- swarm/agents/executive/__init__.py +1 -0
- swarm/agents/intelligence/__init__.py +1 -0
- swarm/agents/intelligence/arbiter.py +30 -0
- swarm/agents/intelligence/sentinel.py +30 -0
- swarm/agents/media/__init__.py +0 -0
- swarm/agents/personal/__init__.py +0 -0
- swarm/agents/personal/atlas.py +27 -0
- swarm/agents/personal/hearth.py +26 -0
- swarm/agents/personal/midas.py +27 -0
- swarm/agents/personal/navigator.py +27 -0
- swarm/agents/personal/nourish.py +31 -0
- swarm/agents/personal/vitals.py +33 -0
- swarm/agents/recursive/__init__.py +0 -0
- swarm/agents/recursive/alchemist.py +73 -0
- swarm/agents/recursive/calibrator.py +45 -0
- swarm/agents/recursive/scout.py +97 -0
- swarm/agents/scientific/__init__.py +0 -0
- swarm/agents/scientific/cosmos.py +36 -0
- swarm/agents/scientific/gaia.py +26 -0
- swarm/agents/scientific/medicus.py +33 -0
- swarm/agents/scientific/oracle_agent.py +39 -0
- swarm/agents/scientific/scribe.py +39 -0
- swarm/agents/talent/__init__.py +0 -0
- swarm/agents/talent/deal.py +28 -0
- swarm/agents/talent/roster.py +29 -0
- swarm/agents/talent/scout.py +29 -0
- swarm/agents/talent/stage.py +27 -0
- swarm/agents/tech/__init__.py +0 -0
- swarm/agents/tech/aegis.py +28 -0
- swarm/agents/tech/bridge.py +28 -0
- swarm/agents/tech/forge.py +28 -0
- swarm/agents/trading/__init__.py +1 -0
- swarm/agents/trading/global_prediction_engine.py +611 -0
- swarm/agents/trading/polymarket_trader.py +165 -0
- swarm/agents/trading/prediction_strategist.py +218 -0
- swarm/autogen_bridge.py +68 -0
- swarm/bid_protocol.py +196 -0
- swarm/dispatcher.py +527 -0
- swarm/nexus.py +181 -0
- swarm/registry.py +112 -0
- telegram_listener.py +403 -0
- ui/tui/app.py +214 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""ZapierConnector — Zapier webhook integration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from integrations.base import (
|
|
10
|
+
BaseConnector,
|
|
11
|
+
ConnectorCapability,
|
|
12
|
+
ConnectorInfo,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("hyperclaw.integrations.zapier")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ZapierConnector(BaseConnector):
|
|
19
|
+
"""
|
|
20
|
+
Zapier webhook connector for triggering Zaps.
|
|
21
|
+
|
|
22
|
+
Required config:
|
|
23
|
+
- webhook_url: Zapier webhook URL
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, config: dict) -> None:
|
|
27
|
+
self.config = config
|
|
28
|
+
self.webhook_url = config.get("webhook_url", "")
|
|
29
|
+
|
|
30
|
+
if config.get("enabled", False) and not self.webhook_url:
|
|
31
|
+
raise ValueError("ZapierConnector requires webhook_url")
|
|
32
|
+
|
|
33
|
+
self._client: httpx.AsyncClient | None = None
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def info(self) -> ConnectorInfo:
|
|
37
|
+
return ConnectorInfo(
|
|
38
|
+
connector_id="zapier",
|
|
39
|
+
platform="zapier",
|
|
40
|
+
category="automation",
|
|
41
|
+
capabilities=frozenset(
|
|
42
|
+
[
|
|
43
|
+
ConnectorCapability.ACTION,
|
|
44
|
+
ConnectorCapability.WEBHOOKS,
|
|
45
|
+
]
|
|
46
|
+
),
|
|
47
|
+
auth_type="webhook_token",
|
|
48
|
+
rate_limit_per_minute=60,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
async def _get_client(self) -> httpx.AsyncClient:
|
|
52
|
+
if self._client is None:
|
|
53
|
+
self._client = httpx.AsyncClient(
|
|
54
|
+
timeout=30.0,
|
|
55
|
+
headers={"Content-Type": "application/json"},
|
|
56
|
+
)
|
|
57
|
+
return self._client
|
|
58
|
+
|
|
59
|
+
async def health(self) -> bool:
|
|
60
|
+
"""Zapier webhooks don't have a health endpoint."""
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
async def trigger_zap(self, data: dict) -> dict:
|
|
64
|
+
"""Trigger a Zap with the given data."""
|
|
65
|
+
client = await self._get_client()
|
|
66
|
+
|
|
67
|
+
response = await client.post(
|
|
68
|
+
self.webhook_url,
|
|
69
|
+
json=data,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Zapier webhooks return various status codes
|
|
73
|
+
if response.status_code not in (200, 201, 202):
|
|
74
|
+
raise Exception(f"Zapier trigger_zap failed: {response.text}")
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
return response.json()
|
|
78
|
+
except Exception:
|
|
79
|
+
return {"status": "triggered", "response": response.text}
|
|
80
|
+
|
|
81
|
+
async def _action_impl(self, action_name: str, params: dict) -> dict:
|
|
82
|
+
"""Execute Zapier-specific actions."""
|
|
83
|
+
if action_name == "trigger" or action_name == "trigger_zap":
|
|
84
|
+
return await self.trigger_zap(params)
|
|
85
|
+
|
|
86
|
+
# Default: pass all params to trigger
|
|
87
|
+
return await self.trigger_zap(params)
|
integrations/base.py
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BaseConnector — foundation for all HyperClaw platform connectors.
|
|
3
|
+
ConnectorRegistry — central registry for connector discovery and lookup.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import importlib
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from typing import Any, AsyncIterator
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("hyperclaw.integrations")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConnectorCapability(str, Enum):
|
|
21
|
+
"""Capabilities that a connector may support."""
|
|
22
|
+
|
|
23
|
+
SEND_MESSAGE = "send_message"
|
|
24
|
+
RECEIVE_MESSAGE = "receive_message"
|
|
25
|
+
READ_DATA = "read_data"
|
|
26
|
+
WRITE_DATA = "write_data"
|
|
27
|
+
DELETE_DATA = "delete_data"
|
|
28
|
+
LIST_DATA = "list_data"
|
|
29
|
+
STREAM = "stream"
|
|
30
|
+
FILE_UPLOAD = "file_upload"
|
|
31
|
+
FILE_DOWNLOAD = "file_download"
|
|
32
|
+
WEBHOOKS = "webhooks"
|
|
33
|
+
OAUTH = "oauth"
|
|
34
|
+
SEARCH = "search"
|
|
35
|
+
NOTIFY = "notify"
|
|
36
|
+
CALENDAR = "calendar"
|
|
37
|
+
ACTION = "action"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class InboundMessage:
|
|
42
|
+
"""Message received from an external platform."""
|
|
43
|
+
|
|
44
|
+
message_id: str
|
|
45
|
+
platform: str
|
|
46
|
+
sender_id: str
|
|
47
|
+
sender_name: str
|
|
48
|
+
content: str
|
|
49
|
+
thread_id: str | None = None
|
|
50
|
+
channel_id: str | None = None
|
|
51
|
+
attachments: list[dict] = field(default_factory=list)
|
|
52
|
+
raw: dict = field(default_factory=dict)
|
|
53
|
+
received_at: datetime = field(default_factory=datetime.utcnow)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class OutboundMessage:
|
|
58
|
+
"""Message to be sent to an external platform."""
|
|
59
|
+
|
|
60
|
+
content: str
|
|
61
|
+
platform: str
|
|
62
|
+
recipient_id: str
|
|
63
|
+
thread_id: str | None = None
|
|
64
|
+
channel_id: str | None = None
|
|
65
|
+
attachments: list[dict] = field(default_factory=list)
|
|
66
|
+
format: str = "text" # "text" | "markdown" | "html" | "blocks"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class ConnectorInfo:
|
|
71
|
+
"""Metadata about a connector's capabilities and limits."""
|
|
72
|
+
|
|
73
|
+
connector_id: str
|
|
74
|
+
platform: str
|
|
75
|
+
category: str # "messaging" | "productivity" | "enterprise" | etc.
|
|
76
|
+
capabilities: frozenset
|
|
77
|
+
auth_type: str # "api_key" | "oauth2" | "webhook_token" | "bearer"
|
|
78
|
+
rate_limit_per_minute: int = 60
|
|
79
|
+
supports_threads: bool = False
|
|
80
|
+
supports_attachments: bool = False
|
|
81
|
+
supports_reactions: bool = False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ConnectorAuthError(Exception):
|
|
85
|
+
"""Authentication failed for connector."""
|
|
86
|
+
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ConnectorRateLimitError(Exception):
|
|
91
|
+
"""Rate limit exceeded for connector."""
|
|
92
|
+
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ConnectorUnavailableError(Exception):
|
|
97
|
+
"""Connector service is unavailable."""
|
|
98
|
+
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class CapabilityNotSupportedError(Exception):
|
|
103
|
+
"""Requested capability is not supported by this connector."""
|
|
104
|
+
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class BaseConnector(ABC):
|
|
109
|
+
"""
|
|
110
|
+
Abstract base class for all HyperClaw platform connectors.
|
|
111
|
+
|
|
112
|
+
Subclasses must implement:
|
|
113
|
+
- info property: returns ConnectorInfo
|
|
114
|
+
- health(): async check if connector is operational
|
|
115
|
+
|
|
116
|
+
Optional implementations depending on capabilities:
|
|
117
|
+
- _send_impl(): for SEND_MESSAGE
|
|
118
|
+
- _receive_impl(): for RECEIVE_MESSAGE
|
|
119
|
+
- _read_impl(): for READ_DATA
|
|
120
|
+
- _write_impl(): for WRITE_DATA
|
|
121
|
+
- _delete_impl(): for DELETE_DATA
|
|
122
|
+
- _list_impl(): for LIST_DATA
|
|
123
|
+
- _search_impl(): for SEARCH
|
|
124
|
+
- _action_impl(): for ACTION
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
# Optional HyperShield security context
|
|
128
|
+
hypershield_context: str | None = None
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
@abstractmethod
|
|
132
|
+
def info(self) -> ConnectorInfo:
|
|
133
|
+
"""Return connector metadata."""
|
|
134
|
+
...
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def connector_id(self) -> str:
|
|
138
|
+
"""Shorthand for info.connector_id."""
|
|
139
|
+
return self.info.connector_id
|
|
140
|
+
|
|
141
|
+
def _taint(self, value: Any) -> Any:
|
|
142
|
+
"""What a connector read returns is connector content (hyperclaw.taint):
|
|
143
|
+
never memory, learning, training or evaluation data."""
|
|
144
|
+
from hyperclaw.taint import mark
|
|
145
|
+
info = self.info
|
|
146
|
+
return mark(value, str(info.platform or "connector").lower(), info.connector_id)
|
|
147
|
+
|
|
148
|
+
def supports(self, cap: ConnectorCapability) -> bool:
|
|
149
|
+
"""Check if this connector supports a capability."""
|
|
150
|
+
return cap in self.info.capabilities
|
|
151
|
+
|
|
152
|
+
def require(self, cap: ConnectorCapability) -> None:
|
|
153
|
+
"""Raise if capability not supported."""
|
|
154
|
+
if not self.supports(cap):
|
|
155
|
+
raise CapabilityNotSupportedError(
|
|
156
|
+
f"{self.connector_id} does not support {cap}"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
@abstractmethod
|
|
160
|
+
async def health(self) -> bool:
|
|
161
|
+
"""Check if connector is operational."""
|
|
162
|
+
...
|
|
163
|
+
|
|
164
|
+
# ── Messaging ──────────────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
async def send(self, message: OutboundMessage) -> str:
|
|
167
|
+
"""Send a message to the platform. Returns message ID."""
|
|
168
|
+
self.require(ConnectorCapability.SEND_MESSAGE)
|
|
169
|
+
return await self._send_impl(message)
|
|
170
|
+
|
|
171
|
+
async def _send_impl(self, message: OutboundMessage) -> str:
|
|
172
|
+
raise NotImplementedError
|
|
173
|
+
|
|
174
|
+
async def receive(self) -> AsyncIterator[InboundMessage]:
|
|
175
|
+
"""Receive messages from the platform."""
|
|
176
|
+
self.require(ConnectorCapability.RECEIVE_MESSAGE)
|
|
177
|
+
async for msg in self._receive_impl():
|
|
178
|
+
msg.content = self._taint(msg.content)
|
|
179
|
+
yield msg
|
|
180
|
+
|
|
181
|
+
async def _receive_impl(self) -> AsyncIterator[InboundMessage]:
|
|
182
|
+
raise NotImplementedError
|
|
183
|
+
yield # make it an async generator
|
|
184
|
+
|
|
185
|
+
# ── Data Operations ────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
async def read(self, resource_id: str, **kwargs) -> dict:
|
|
188
|
+
"""Read a resource from the platform."""
|
|
189
|
+
self.require(ConnectorCapability.READ_DATA)
|
|
190
|
+
return self._taint(await self._read_impl(resource_id, **kwargs))
|
|
191
|
+
|
|
192
|
+
async def _read_impl(self, resource_id: str, **kwargs) -> dict:
|
|
193
|
+
raise NotImplementedError
|
|
194
|
+
|
|
195
|
+
async def write(self, resource_type: str, data: dict, **kwargs) -> dict:
|
|
196
|
+
"""Write/create a resource on the platform."""
|
|
197
|
+
self.require(ConnectorCapability.WRITE_DATA)
|
|
198
|
+
return await self._write_impl(resource_type, data, **kwargs)
|
|
199
|
+
|
|
200
|
+
async def _write_impl(self, resource_type: str, data: dict, **kwargs) -> dict:
|
|
201
|
+
raise NotImplementedError
|
|
202
|
+
|
|
203
|
+
async def delete(self, resource_id: str, **kwargs) -> bool:
|
|
204
|
+
"""Delete a resource from the platform."""
|
|
205
|
+
self.require(ConnectorCapability.DELETE_DATA)
|
|
206
|
+
return await self._delete_impl(resource_id, **kwargs)
|
|
207
|
+
|
|
208
|
+
async def _delete_impl(self, resource_id: str, **kwargs) -> bool:
|
|
209
|
+
raise NotImplementedError
|
|
210
|
+
|
|
211
|
+
async def list(
|
|
212
|
+
self, resource_type: str, filters: dict | None = None, **kwargs
|
|
213
|
+
) -> list[dict]:
|
|
214
|
+
"""List resources of a given type."""
|
|
215
|
+
self.require(ConnectorCapability.LIST_DATA)
|
|
216
|
+
return self._taint(await self._list_impl(resource_type, filters or {}, **kwargs))
|
|
217
|
+
|
|
218
|
+
async def _list_impl(
|
|
219
|
+
self, resource_type: str, filters: dict, **kwargs
|
|
220
|
+
) -> list[dict]:
|
|
221
|
+
raise NotImplementedError
|
|
222
|
+
|
|
223
|
+
async def search(self, query: str, **kwargs) -> list[dict]:
|
|
224
|
+
"""Search for resources."""
|
|
225
|
+
self.require(ConnectorCapability.SEARCH)
|
|
226
|
+
return self._taint(await self._search_impl(query, **kwargs))
|
|
227
|
+
|
|
228
|
+
async def _search_impl(self, query: str, **kwargs) -> list[dict]:
|
|
229
|
+
raise NotImplementedError
|
|
230
|
+
|
|
231
|
+
async def action(self, action_name: str, params: dict | None = None) -> dict:
|
|
232
|
+
"""Execute a platform-specific action."""
|
|
233
|
+
self.require(ConnectorCapability.ACTION)
|
|
234
|
+
return await self._action_impl(action_name, params or {})
|
|
235
|
+
|
|
236
|
+
async def _action_impl(self, action_name: str, params: dict) -> dict:
|
|
237
|
+
raise NotImplementedError
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class ConnectorRegistry:
|
|
241
|
+
"""Central registry of all HyperClaw connectors."""
|
|
242
|
+
|
|
243
|
+
def __init__(self) -> None:
|
|
244
|
+
self._connectors: dict[str, BaseConnector] = {}
|
|
245
|
+
|
|
246
|
+
def register(self, connector: BaseConnector) -> None:
|
|
247
|
+
"""Register a connector instance."""
|
|
248
|
+
self._connectors[connector.connector_id] = connector
|
|
249
|
+
|
|
250
|
+
def unregister(self, connector_id: str) -> None:
|
|
251
|
+
"""Remove a connector from the registry."""
|
|
252
|
+
self._connectors.pop(connector_id, None)
|
|
253
|
+
|
|
254
|
+
def get(self, connector_id: str) -> BaseConnector:
|
|
255
|
+
"""Get a connector by ID. Raises KeyError if not found."""
|
|
256
|
+
if connector_id not in self._connectors:
|
|
257
|
+
raise KeyError(f"Connector not found: {connector_id}")
|
|
258
|
+
return self._connectors[connector_id]
|
|
259
|
+
|
|
260
|
+
def has(self, connector_id: str) -> bool:
|
|
261
|
+
"""Check if a connector is registered."""
|
|
262
|
+
return connector_id in self._connectors
|
|
263
|
+
|
|
264
|
+
def list_all(self) -> list[BaseConnector]:
|
|
265
|
+
"""List all registered connectors."""
|
|
266
|
+
return list(self._connectors.values())
|
|
267
|
+
|
|
268
|
+
def list_by_category(self, category: str) -> list[BaseConnector]:
|
|
269
|
+
"""List connectors in a specific category."""
|
|
270
|
+
return [c for c in self._connectors.values() if c.info.category == category]
|
|
271
|
+
|
|
272
|
+
def list_by_capability(self, cap: ConnectorCapability) -> list[BaseConnector]:
|
|
273
|
+
"""List connectors that support a capability."""
|
|
274
|
+
return [c for c in self._connectors.values() if c.supports(cap)]
|
|
275
|
+
|
|
276
|
+
def get_messaging_connectors(self) -> list[BaseConnector]:
|
|
277
|
+
"""Shorthand for messaging category."""
|
|
278
|
+
return self.list_by_category("messaging")
|
|
279
|
+
|
|
280
|
+
@classmethod
|
|
281
|
+
def build_from_config(cls, config_path: str) -> "ConnectorRegistry":
|
|
282
|
+
"""
|
|
283
|
+
Reads integrations/config/integrations.yaml.
|
|
284
|
+
Instantiates and registers all enabled connectors.
|
|
285
|
+
Skips connectors with missing credentials without crashing.
|
|
286
|
+
"""
|
|
287
|
+
import yaml
|
|
288
|
+
|
|
289
|
+
registry = cls()
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
with open(config_path) as f:
|
|
293
|
+
config = yaml.safe_load(f)
|
|
294
|
+
except Exception as e:
|
|
295
|
+
logger.warning(f"Could not load integrations config: {e}")
|
|
296
|
+
return registry
|
|
297
|
+
|
|
298
|
+
if not config:
|
|
299
|
+
return registry
|
|
300
|
+
|
|
301
|
+
connector_map = _get_connector_map()
|
|
302
|
+
|
|
303
|
+
def _resolve_env(value: Any) -> Any:
|
|
304
|
+
"""Resolve ${ENV_VAR} references."""
|
|
305
|
+
if isinstance(value, str) and value.startswith("${") and value.endswith("}"):
|
|
306
|
+
env_key = value[2:-1]
|
|
307
|
+
return os.environ.get(env_key, "")
|
|
308
|
+
return value
|
|
309
|
+
|
|
310
|
+
def _resolve_config(cfg: Any) -> Any:
|
|
311
|
+
"""Recursively resolve env vars in config dict."""
|
|
312
|
+
if isinstance(cfg, dict):
|
|
313
|
+
return {k: _resolve_config(v) for k, v in cfg.items()}
|
|
314
|
+
elif isinstance(cfg, list):
|
|
315
|
+
return [_resolve_config(v) for v in cfg]
|
|
316
|
+
else:
|
|
317
|
+
return _resolve_env(cfg)
|
|
318
|
+
|
|
319
|
+
def _try_register(
|
|
320
|
+
connector_id: str, connector_class: type, connector_config: dict
|
|
321
|
+
) -> None:
|
|
322
|
+
resolved = _resolve_config(connector_config)
|
|
323
|
+
try:
|
|
324
|
+
connector = connector_class(resolved)
|
|
325
|
+
registry.register(connector)
|
|
326
|
+
logger.info(f"Registered connector: {connector_id}")
|
|
327
|
+
except Exception as e:
|
|
328
|
+
logger.warning(f"Skipping connector {connector_id}: {e}")
|
|
329
|
+
|
|
330
|
+
_walk_config_and_register(config, connector_map, _try_register)
|
|
331
|
+
|
|
332
|
+
return registry
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _get_connector_map() -> dict[str, type]:
|
|
336
|
+
"""Returns mapping of connector_id -> connector class."""
|
|
337
|
+
connectors: dict[str, type] = {}
|
|
338
|
+
_try_import_connectors(connectors)
|
|
339
|
+
return connectors
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _try_import_connectors(connectors: dict[str, type]) -> None:
|
|
343
|
+
"""Try to import all connector classes, skip if import fails."""
|
|
344
|
+
imports = [
|
|
345
|
+
# Messaging
|
|
346
|
+
("telegram", "integrations.messaging.telegram", "TelegramConnector"),
|
|
347
|
+
("slack", "integrations.messaging.slack", "SlackConnector"),
|
|
348
|
+
("discord", "integrations.messaging.discord", "DiscordConnector"),
|
|
349
|
+
("whatsapp", "integrations.messaging.whatsapp", "WhatsAppConnector"),
|
|
350
|
+
("teams", "integrations.messaging.teams", "TeamsConnector"),
|
|
351
|
+
("signal", "integrations.messaging.signal", "SignalConnector"),
|
|
352
|
+
("imessage", "integrations.messaging.imessage", "iMessageConnector"),
|
|
353
|
+
("sms", "integrations.messaging.sms", "SMSConnector"),
|
|
354
|
+
("email", "integrations.messaging.email", "EmailConnector"),
|
|
355
|
+
# Google Workspace
|
|
356
|
+
("gmail", "integrations.productivity.google.gmail", "GmailConnector"),
|
|
357
|
+
(
|
|
358
|
+
"google_calendar",
|
|
359
|
+
"integrations.productivity.google.calendar",
|
|
360
|
+
"GoogleCalendarConnector",
|
|
361
|
+
),
|
|
362
|
+
(
|
|
363
|
+
"google_drive",
|
|
364
|
+
"integrations.productivity.google.drive",
|
|
365
|
+
"GoogleDriveConnector",
|
|
366
|
+
),
|
|
367
|
+
(
|
|
368
|
+
"google_docs",
|
|
369
|
+
"integrations.productivity.google.docs",
|
|
370
|
+
"GoogleDocsConnector",
|
|
371
|
+
),
|
|
372
|
+
(
|
|
373
|
+
"google_sheets",
|
|
374
|
+
"integrations.productivity.google.sheets",
|
|
375
|
+
"GoogleSheetsConnector",
|
|
376
|
+
),
|
|
377
|
+
(
|
|
378
|
+
"google_meet",
|
|
379
|
+
"integrations.productivity.google.meet",
|
|
380
|
+
"GoogleMeetConnector",
|
|
381
|
+
),
|
|
382
|
+
(
|
|
383
|
+
"google_tasks",
|
|
384
|
+
"integrations.productivity.google.tasks",
|
|
385
|
+
"GoogleTasksConnector",
|
|
386
|
+
),
|
|
387
|
+
# Microsoft 365
|
|
388
|
+
("outlook", "integrations.productivity.microsoft.outlook", "OutlookConnector"),
|
|
389
|
+
(
|
|
390
|
+
"onedrive",
|
|
391
|
+
"integrations.productivity.microsoft.onedrive",
|
|
392
|
+
"OneDriveConnector",
|
|
393
|
+
),
|
|
394
|
+
(
|
|
395
|
+
"sharepoint",
|
|
396
|
+
"integrations.productivity.microsoft.sharepoint",
|
|
397
|
+
"SharePointConnector",
|
|
398
|
+
),
|
|
399
|
+
(
|
|
400
|
+
"microsoft_calendar",
|
|
401
|
+
"integrations.productivity.microsoft.calendar",
|
|
402
|
+
"MicrosoftCalendarConnector",
|
|
403
|
+
),
|
|
404
|
+
# Productivity
|
|
405
|
+
("notion", "integrations.productivity.notion", "NotionConnector"),
|
|
406
|
+
("airtable", "integrations.productivity.airtable", "AirtableConnector"),
|
|
407
|
+
("trello", "integrations.productivity.trello", "TrelloConnector"),
|
|
408
|
+
("asana", "integrations.productivity.asana", "AsanaConnector"),
|
|
409
|
+
("linear", "integrations.productivity.linear", "LinearConnector"),
|
|
410
|
+
("todoist", "integrations.productivity.todoist", "TodoistConnector"),
|
|
411
|
+
# Enterprise
|
|
412
|
+
("salesforce", "integrations.enterprise.salesforce", "SalesforceConnector"),
|
|
413
|
+
("hubspot", "integrations.enterprise.hubspot", "HubSpotConnector"),
|
|
414
|
+
("jira", "integrations.enterprise.jira", "JiraConnector"),
|
|
415
|
+
("confluence", "integrations.enterprise.confluence", "ConfluenceConnector"),
|
|
416
|
+
# Developer
|
|
417
|
+
("github", "integrations.developer.github", "GitHubConnector"),
|
|
418
|
+
("gitlab", "integrations.developer.gitlab", "GitLabConnector"),
|
|
419
|
+
# Storage
|
|
420
|
+
("box", "integrations.storage.box", "BoxConnector"),
|
|
421
|
+
("dropbox", "integrations.storage.dropbox", "DropboxConnector"),
|
|
422
|
+
# Finance
|
|
423
|
+
("stripe", "integrations.finance.stripe", "StripeConnector"),
|
|
424
|
+
("quickbooks", "integrations.finance.quickbooks", "QuickBooksConnector"),
|
|
425
|
+
# Communication
|
|
426
|
+
("zoom", "integrations.communication.zoom", "ZoomConnector"),
|
|
427
|
+
("twilio", "integrations.communication.twilio", "TwilioConnector"),
|
|
428
|
+
# Automation
|
|
429
|
+
("zapier", "integrations.automation.zapier", "ZapierConnector"),
|
|
430
|
+
("make", "integrations.automation.make", "MakeConnector"),
|
|
431
|
+
# Data
|
|
432
|
+
("supabase", "integrations.data.supabase_connector", "SupabaseConnector"),
|
|
433
|
+
("postgres", "integrations.data.postgres", "PostgresConnector"),
|
|
434
|
+
(
|
|
435
|
+
"google_analytics",
|
|
436
|
+
"integrations.data.google_analytics",
|
|
437
|
+
"GoogleAnalyticsConnector",
|
|
438
|
+
),
|
|
439
|
+
]
|
|
440
|
+
|
|
441
|
+
for connector_id, module_path, class_name in imports:
|
|
442
|
+
try:
|
|
443
|
+
mod = importlib.import_module(module_path)
|
|
444
|
+
connectors[connector_id] = getattr(mod, class_name)
|
|
445
|
+
except Exception as e:
|
|
446
|
+
logger.debug(f"Could not import {connector_id}: {e}")
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def _walk_config_and_register(
|
|
450
|
+
config: dict,
|
|
451
|
+
connector_map: dict[str, type],
|
|
452
|
+
register_fn: callable,
|
|
453
|
+
) -> None:
|
|
454
|
+
"""Walk nested config dict, find enabled connectors, register them."""
|
|
455
|
+
for section_key, section_val in config.items():
|
|
456
|
+
if not isinstance(section_val, dict):
|
|
457
|
+
continue
|
|
458
|
+
|
|
459
|
+
for connector_id, connector_cfg in section_val.items():
|
|
460
|
+
if not isinstance(connector_cfg, dict):
|
|
461
|
+
continue
|
|
462
|
+
|
|
463
|
+
# Direct connector config
|
|
464
|
+
if connector_cfg.get("enabled", False) and connector_id in connector_map:
|
|
465
|
+
register_fn(connector_id, connector_map[connector_id], connector_cfg)
|
|
466
|
+
# Handle nested sections (e.g. google.gmail, microsoft.outlook)
|
|
467
|
+
elif isinstance(connector_cfg, dict):
|
|
468
|
+
for sub_id, sub_cfg in connector_cfg.items():
|
|
469
|
+
if isinstance(sub_cfg, dict) and sub_cfg.get("enabled", False):
|
|
470
|
+
# Try sub_id first (e.g., "gmail"), then full path
|
|
471
|
+
full_id = f"{connector_id}_{sub_id}"
|
|
472
|
+
cls = connector_map.get(sub_id) or connector_map.get(full_id)
|
|
473
|
+
if cls:
|
|
474
|
+
# Merge parent config with sub config
|
|
475
|
+
merged = {**connector_cfg, **sub_cfg}
|
|
476
|
+
register_fn(sub_id, cls, merged)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Communication integrations
|