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
hyperclaw/feeds.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HyperClaw Live Feeds — Markets, Intel, Polymarket
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
from datetime import datetime, timedelta
|
|
8
|
+
from typing import Optional
|
|
9
|
+
import httpx
|
|
10
|
+
import feedparser
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("hyperclaw.feeds")
|
|
13
|
+
|
|
14
|
+
# ── Market Symbols ────────────────────────────────────────────────────────────
|
|
15
|
+
MARKET_SYMBOLS = {
|
|
16
|
+
# Crypto
|
|
17
|
+
"BTC-USD": "Bitcoin",
|
|
18
|
+
"ETH-USD": "Ethereum",
|
|
19
|
+
"SOL-USD": "Solana",
|
|
20
|
+
"AVAX-USD": "Avalanche",
|
|
21
|
+
"LINK-USD": "Chainlink",
|
|
22
|
+
"MATIC-USD": "Polygon",
|
|
23
|
+
"DOT-USD": "Polkadot",
|
|
24
|
+
"ATOM-USD": "Cosmos",
|
|
25
|
+
"UNI-USD": "Uniswap",
|
|
26
|
+
"AAVE-USD": "Aave",
|
|
27
|
+
# Major Indices
|
|
28
|
+
"^GSPC": "S&P 500",
|
|
29
|
+
"^DJI": "Dow Jones",
|
|
30
|
+
"^IXIC": "Nasdaq",
|
|
31
|
+
"^RUT": "Russell 2000",
|
|
32
|
+
"^VIX": "VIX",
|
|
33
|
+
# Mega Caps
|
|
34
|
+
"AAPL": "Apple",
|
|
35
|
+
"MSFT": "Microsoft",
|
|
36
|
+
"GOOGL": "Alphabet",
|
|
37
|
+
"AMZN": "Amazon",
|
|
38
|
+
"NVDA": "NVIDIA",
|
|
39
|
+
"META": "Meta",
|
|
40
|
+
"TSLA": "Tesla",
|
|
41
|
+
"BRK-B": "Berkshire",
|
|
42
|
+
# Tech
|
|
43
|
+
"AMD": "AMD",
|
|
44
|
+
"INTC": "Intel",
|
|
45
|
+
"CRM": "Salesforce",
|
|
46
|
+
"ORCL": "Oracle",
|
|
47
|
+
"ADBE": "Adobe",
|
|
48
|
+
"NFLX": "Netflix",
|
|
49
|
+
"PYPL": "PayPal",
|
|
50
|
+
"SQ": "Block",
|
|
51
|
+
"SHOP": "Shopify",
|
|
52
|
+
"SNOW": "Snowflake",
|
|
53
|
+
"PLTR": "Palantir",
|
|
54
|
+
"COIN": "Coinbase",
|
|
55
|
+
# Finance
|
|
56
|
+
"JPM": "JPMorgan",
|
|
57
|
+
"GS": "Goldman",
|
|
58
|
+
"MS": "Morgan Stanley",
|
|
59
|
+
"BAC": "Bank of America",
|
|
60
|
+
"V": "Visa",
|
|
61
|
+
"MA": "Mastercard",
|
|
62
|
+
# Healthcare
|
|
63
|
+
"UNH": "UnitedHealth",
|
|
64
|
+
"JNJ": "J&J",
|
|
65
|
+
"PFE": "Pfizer",
|
|
66
|
+
"MRNA": "Moderna",
|
|
67
|
+
"LLY": "Eli Lilly",
|
|
68
|
+
# Commodities
|
|
69
|
+
"GC=F": "Gold",
|
|
70
|
+
"SI=F": "Silver",
|
|
71
|
+
"CL=F": "Crude Oil",
|
|
72
|
+
"NG=F": "Natural Gas",
|
|
73
|
+
# Forex
|
|
74
|
+
"DX-Y.NYB": "USD Index",
|
|
75
|
+
"EURUSD=X": "EUR/USD",
|
|
76
|
+
"GBPUSD=X": "GBP/USD",
|
|
77
|
+
"USDJPY=X": "USD/JPY",
|
|
78
|
+
# ETFs
|
|
79
|
+
"SPY": "S&P 500 ETF",
|
|
80
|
+
"QQQ": "Nasdaq ETF",
|
|
81
|
+
"IWM": "Russell 2000 ETF",
|
|
82
|
+
"GLD": "Gold ETF",
|
|
83
|
+
"TLT": "Treasury ETF",
|
|
84
|
+
"XLF": "Financials ETF",
|
|
85
|
+
"XLE": "Energy ETF",
|
|
86
|
+
"ARKK": "ARK Innovation",
|
|
87
|
+
# Space & Defense
|
|
88
|
+
"LMT": "Lockheed",
|
|
89
|
+
"RTX": "Raytheon",
|
|
90
|
+
"BA": "Boeing",
|
|
91
|
+
"RKLB": "Rocket Lab",
|
|
92
|
+
# Travel & Hospitality
|
|
93
|
+
"MAR": "Marriott",
|
|
94
|
+
"HLT": "Hilton",
|
|
95
|
+
"H": "Hyatt",
|
|
96
|
+
"ABNB": "Airbnb",
|
|
97
|
+
"BKNG": "Booking",
|
|
98
|
+
"EXPE": "Expedia",
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# ── Intel RSS Feeds (60+ sources) ─────────────────────────────────────────────
|
|
102
|
+
INTEL_FEEDS = [
|
|
103
|
+
# Tier 1 — Major Wire Services
|
|
104
|
+
("Reuters", "https://feeds.reuters.com/reuters/topNews"),
|
|
105
|
+
("AP News", "https://rsshub.app/apnews/topics/apf-topnews"),
|
|
106
|
+
# Tier 2 — Business/Finance
|
|
107
|
+
("Bloomberg", "https://feeds.bloomberg.com/markets/news.rss"),
|
|
108
|
+
("WSJ", "https://feeds.a]arstechnica.com/arstechnica/index"),
|
|
109
|
+
("FT", "https://www.ft.com/rss/home"),
|
|
110
|
+
("CNBC", "https://www.cnbc.com/id/100003114/device/rss/rss.html"),
|
|
111
|
+
("MarketWatch", "https://feeds.marketwatch.com/marketwatch/topstories"),
|
|
112
|
+
("Barron's", "https://www.barrons.com/feed"),
|
|
113
|
+
# Tier 3 — Tech
|
|
114
|
+
("TechCrunch", "https://techcrunch.com/feed/"),
|
|
115
|
+
("Ars Technica", "https://feeds.arstechnica.com/arstechnica/index"),
|
|
116
|
+
("The Verge", "https://www.theverge.com/rss/index.xml"),
|
|
117
|
+
("Wired", "https://www.wired.com/feed/rss"),
|
|
118
|
+
("MIT Tech Review", "https://www.technologyreview.com/feed/"),
|
|
119
|
+
("Hacker News", "https://news.ycombinator.com/rss"),
|
|
120
|
+
# Tier 4 — AI/ML
|
|
121
|
+
("AI News", "https://www.artificialintelligence-news.com/feed/"),
|
|
122
|
+
("OpenAI Blog", "https://openai.com/blog/rss/"),
|
|
123
|
+
("DeepMind", "https://deepmind.com/blog/feed/basic/"),
|
|
124
|
+
# Tier 5 — Crypto
|
|
125
|
+
("CoinDesk", "https://www.coindesk.com/arc/outboundfeeds/rss/"),
|
|
126
|
+
("The Block", "https://www.theblock.co/rss.xml"),
|
|
127
|
+
("Decrypt", "https://decrypt.co/feed"),
|
|
128
|
+
# Tier 6 — Science/Space
|
|
129
|
+
("NASA", "https://www.nasa.gov/rss/dyn/breaking_news.rss"),
|
|
130
|
+
("SpaceNews", "https://spacenews.com/feed/"),
|
|
131
|
+
("Nature", "https://www.nature.com/nature.rss"),
|
|
132
|
+
("Science Daily", "https://www.sciencedaily.com/rss/all.xml"),
|
|
133
|
+
# Tier 7 — Geopolitics
|
|
134
|
+
("Foreign Affairs", "https://www.foreignaffairs.com/rss.xml"),
|
|
135
|
+
("The Economist", "https://www.economist.com/international/rss.xml"),
|
|
136
|
+
("Politico", "https://www.politico.com/rss/politicopicks.xml"),
|
|
137
|
+
("Defense One", "https://www.defenseone.com/rss/all/"),
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
YAHOO_HEADERS = {
|
|
142
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
|
|
143
|
+
"Accept": "application/json",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
async def fetch_yahoo_quote(symbol: str, client: httpx.AsyncClient) -> Optional[dict]:
|
|
148
|
+
"""Fetch a single quote from Yahoo Finance."""
|
|
149
|
+
try:
|
|
150
|
+
url = f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}"
|
|
151
|
+
params = {"interval": "1d", "range": "5d"}
|
|
152
|
+
resp = await client.get(url, params=params, headers=YAHOO_HEADERS, timeout=8.0)
|
|
153
|
+
if resp.status_code == 429:
|
|
154
|
+
# Rate limited - wait and retry once
|
|
155
|
+
await asyncio.sleep(1)
|
|
156
|
+
resp = await client.get(url, params=params, headers=YAHOO_HEADERS, timeout=8.0)
|
|
157
|
+
if resp.status_code != 200:
|
|
158
|
+
return None
|
|
159
|
+
data = resp.json()
|
|
160
|
+
result = data.get("chart", {}).get("result", [])
|
|
161
|
+
if not result:
|
|
162
|
+
return None
|
|
163
|
+
meta = result[0].get("meta", {})
|
|
164
|
+
price = meta.get("regularMarketPrice", 0)
|
|
165
|
+
prev_close = meta.get("chartPreviousClose") or meta.get("previousClose") or meta.get("regularMarketPreviousClose") or price
|
|
166
|
+
change_pct = ((price - prev_close) / prev_close * 100) if prev_close and prev_close != price else 0
|
|
167
|
+
|
|
168
|
+
# Get sparkline data
|
|
169
|
+
indicators = result[0].get("indicators", {}).get("quote", [{}])[0]
|
|
170
|
+
closes = indicators.get("close", [])
|
|
171
|
+
sparkline = [c for c in closes if c is not None][-20:] if closes else []
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
"symbol": symbol.replace("-USD", "").replace("=F", "").replace("^", ""),
|
|
175
|
+
"name": MARKET_SYMBOLS.get(symbol, symbol),
|
|
176
|
+
"price": round(price, 2),
|
|
177
|
+
"change": round(change_pct, 2),
|
|
178
|
+
"sparkline": sparkline,
|
|
179
|
+
}
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.debug(f"Yahoo quote error for {symbol}: {e}")
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async def get_markets(symbols: Optional[list] = None) -> list[dict]:
|
|
186
|
+
"""Fetch live market data using parallel v8/chart API calls."""
|
|
187
|
+
if symbols is None:
|
|
188
|
+
symbols = list(MARKET_SYMBOLS.keys())[:25] # Top 25 for performance
|
|
189
|
+
|
|
190
|
+
async with httpx.AsyncClient() as client:
|
|
191
|
+
# Fire all requests concurrently with semaphore to avoid hammering
|
|
192
|
+
sem = asyncio.Semaphore(8)
|
|
193
|
+
async def fetch_one(sym):
|
|
194
|
+
async with sem:
|
|
195
|
+
return await fetch_yahoo_quote(sym, client)
|
|
196
|
+
|
|
197
|
+
results = await asyncio.gather(*[fetch_one(s) for s in symbols])
|
|
198
|
+
return [r for r in results if r is not None]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
async def fetch_rss_feed(name: str, url: str, client: httpx.AsyncClient) -> list[dict]:
|
|
202
|
+
"""Fetch and parse a single RSS feed."""
|
|
203
|
+
try:
|
|
204
|
+
resp = await client.get(url, timeout=8.0, follow_redirects=True)
|
|
205
|
+
if resp.status_code != 200:
|
|
206
|
+
return []
|
|
207
|
+
feed = feedparser.parse(resp.text)
|
|
208
|
+
items = []
|
|
209
|
+
for entry in feed.entries[:5]: # Top 5 per source
|
|
210
|
+
published = entry.get("published_parsed") or entry.get("updated_parsed")
|
|
211
|
+
if published:
|
|
212
|
+
pub_time = datetime(*published[:6])
|
|
213
|
+
delta = datetime.utcnow() - pub_time
|
|
214
|
+
if delta.days > 0:
|
|
215
|
+
time_str = f"{delta.days}D AGO"
|
|
216
|
+
elif delta.seconds >= 3600:
|
|
217
|
+
time_str = f"{delta.seconds // 3600}H AGO"
|
|
218
|
+
else:
|
|
219
|
+
time_str = f"{delta.seconds // 60}M AGO"
|
|
220
|
+
else:
|
|
221
|
+
time_str = "RECENT"
|
|
222
|
+
|
|
223
|
+
items.append({
|
|
224
|
+
"source": name.upper(),
|
|
225
|
+
"title": entry.get("title", "")[:120],
|
|
226
|
+
"summary": (entry.get("summary") or entry.get("description") or "")[:200].strip(),
|
|
227
|
+
"link": entry.get("link", ""),
|
|
228
|
+
"time": time_str,
|
|
229
|
+
"timestamp": pub_time.isoformat() if published else None,
|
|
230
|
+
})
|
|
231
|
+
return items
|
|
232
|
+
except Exception as e:
|
|
233
|
+
logger.debug(f"RSS error for {name}: {e}")
|
|
234
|
+
return []
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
async def get_intel(limit: int = 30) -> list[dict]:
|
|
238
|
+
"""Aggregate intel from all RSS feeds."""
|
|
239
|
+
async with httpx.AsyncClient() as client:
|
|
240
|
+
tasks = [fetch_rss_feed(name, url, client) for name, url in INTEL_FEEDS]
|
|
241
|
+
results = await asyncio.gather(*tasks)
|
|
242
|
+
|
|
243
|
+
all_items = []
|
|
244
|
+
for items in results:
|
|
245
|
+
all_items.extend(items)
|
|
246
|
+
|
|
247
|
+
# Sort by timestamp (most recent first), filter out items without timestamps
|
|
248
|
+
with_ts = [i for i in all_items if i.get("timestamp")]
|
|
249
|
+
with_ts.sort(key=lambda x: x["timestamp"], reverse=True)
|
|
250
|
+
|
|
251
|
+
return with_ts[:limit]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
async def get_polymarket(limit: int = 20) -> list[dict]:
|
|
255
|
+
"""Fetch live Polymarket prediction markets."""
|
|
256
|
+
try:
|
|
257
|
+
async with httpx.AsyncClient() as client:
|
|
258
|
+
# Polymarket Gamma API (better for browsing markets)
|
|
259
|
+
url = "https://gamma-api.polymarket.com/markets"
|
|
260
|
+
params = {"limit": 100, "active": "true", "closed": "false", "order": "volume", "ascending": "false"}
|
|
261
|
+
resp = await client.get(url, params=params, timeout=10.0)
|
|
262
|
+
if resp.status_code != 200:
|
|
263
|
+
# Fallback to CLOB API
|
|
264
|
+
url = "https://clob.polymarket.com/markets"
|
|
265
|
+
params = {"limit": 50, "active": "true"}
|
|
266
|
+
resp = await client.get(url, params=params, timeout=10.0)
|
|
267
|
+
if resp.status_code != 200:
|
|
268
|
+
return []
|
|
269
|
+
|
|
270
|
+
raw = resp.json()
|
|
271
|
+
# Handle both API structures
|
|
272
|
+
data = raw.get("data", raw) if isinstance(raw, dict) else raw
|
|
273
|
+
if not isinstance(data, list):
|
|
274
|
+
data = [data] if data else []
|
|
275
|
+
|
|
276
|
+
markets = []
|
|
277
|
+
for m in data:
|
|
278
|
+
if m.get("closed") or m.get("archived"):
|
|
279
|
+
continue
|
|
280
|
+
question = m.get("question", "") or m.get("title", "")
|
|
281
|
+
if not question or len(question) < 10:
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
# Get probability from various possible fields
|
|
285
|
+
prob = 50
|
|
286
|
+
if "outcomePrices" in m:
|
|
287
|
+
try:
|
|
288
|
+
prices = m["outcomePrices"]
|
|
289
|
+
if isinstance(prices, str):
|
|
290
|
+
import json
|
|
291
|
+
prices = json.loads(prices)
|
|
292
|
+
if prices and len(prices) > 0:
|
|
293
|
+
prob = int(float(prices[0]) * 100)
|
|
294
|
+
except:
|
|
295
|
+
pass
|
|
296
|
+
elif "tokens" in m and m["tokens"]:
|
|
297
|
+
try:
|
|
298
|
+
prob = int(float(m["tokens"][0].get("price", 0.5)) * 100)
|
|
299
|
+
except:
|
|
300
|
+
pass
|
|
301
|
+
|
|
302
|
+
vol = m.get("volume", 0) or m.get("volumeNum", 0) or 0
|
|
303
|
+
try:
|
|
304
|
+
vol = float(vol)
|
|
305
|
+
except:
|
|
306
|
+
vol = 0
|
|
307
|
+
vol_str = f"${vol/1e6:.1f}M" if vol >= 1e6 else f"${vol/1e3:.0f}K" if vol >= 1000 else f"${vol:.0f}"
|
|
308
|
+
|
|
309
|
+
markets.append({
|
|
310
|
+
"question": question[:150],
|
|
311
|
+
"prob": max(1, min(99, prob)),
|
|
312
|
+
"volume": vol_str,
|
|
313
|
+
"end_date": m.get("endDate") or m.get("end_date_iso"),
|
|
314
|
+
"slug": m.get("slug") or m.get("condition_id", ""),
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
# Sort by volume (already sorted but ensure)
|
|
318
|
+
markets.sort(key=lambda x: float(x["volume"].replace("$","").replace("M","000000").replace("K","000")), reverse=True)
|
|
319
|
+
return markets[:limit]
|
|
320
|
+
except Exception as e:
|
|
321
|
+
logger.error(f"Polymarket API error: {e}")
|
|
322
|
+
return []
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# ── Summit Calendar ───────────────────────────────────────────────────────────
|
|
326
|
+
SUMMITS = [
|
|
327
|
+
{"month": "JUN", "day": "12", "name": "BILDERBERG 2026", "location": "Madrid, Spain", "date": "2026-06-12"},
|
|
328
|
+
{"month": "JUL", "day": "09", "name": "SUN VALLEY CONFERENCE", "location": "Sun Valley, Idaho", "date": "2026-07-09"},
|
|
329
|
+
{"month": "SEP", "day": "17", "name": "UN GENERAL ASSEMBLY", "location": "New York, NY", "date": "2026-09-17"},
|
|
330
|
+
{"month": "NOV", "day": "15", "name": "G20 SUMMIT", "location": "South Africa", "date": "2026-11-15"},
|
|
331
|
+
{"month": "JAN", "day": "20", "name": "WEF DAVOS 2027", "location": "Davos, Switzerland", "date": "2027-01-20"},
|
|
332
|
+
{"month": "FEB", "day": "17", "name": "MUNICH SECURITY CONF", "location": "Munich, Germany", "date": "2027-02-17"},
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def get_summits() -> list[dict]:
|
|
337
|
+
"""Return upcoming summits."""
|
|
338
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
339
|
+
return [s for s in SUMMITS if s["date"] >= today]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"""Google Workspace for HyperAgents: Gmail and Calendar first, then Drive,
|
|
2
|
+
Docs, Sheets, Slides and Meet (read). One sign-in per person through the
|
|
3
|
+
team's internal Google app; per-install tokens; every non-read tool behind
|
|
4
|
+
the owner's approval; every read result tainted (Limited Use) and screened
|
|
5
|
+
by the restricted-client guard (hyperclaw/client_guard.py)."""
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""HTTP to Google's Workspace APIs for one install's own account.
|
|
2
|
+
|
|
3
|
+
Reads are GET only. Writes (a Gmail draft, a Gmail send, a Calendar event)
|
|
4
|
+
use POST/PATCH and are reachable only from ask-tier tools, i.e. after the
|
|
5
|
+
owner pressed Send on the exact payload. Requests go only to Google's API
|
|
6
|
+
hosts below. No token, URL or response body is ever logged: reason codes only.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any, Callable, Optional
|
|
13
|
+
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
from .errors import GoogleError, Reason
|
|
17
|
+
|
|
18
|
+
log = logging.getLogger("hyperclaw.google")
|
|
19
|
+
|
|
20
|
+
BASES = {
|
|
21
|
+
"gmail": "https://gmail.googleapis.com/gmail/v1",
|
|
22
|
+
"calendar": "https://www.googleapis.com/calendar/v3",
|
|
23
|
+
"drive": "https://www.googleapis.com/drive/v3",
|
|
24
|
+
"docs": "https://docs.googleapis.com/v1",
|
|
25
|
+
"sheets": "https://sheets.googleapis.com/v4",
|
|
26
|
+
"slides": "https://slides.googleapis.com/v1",
|
|
27
|
+
"meet": "https://meet.googleapis.com/v2",
|
|
28
|
+
}
|
|
29
|
+
TIMEOUT = 30.0
|
|
30
|
+
MAX_RETRIES = 2
|
|
31
|
+
MAX_WAIT = 20.0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class GoogleApi:
|
|
35
|
+
"""One install's Google client. ``token_fn(force_refresh=False)`` gives
|
|
36
|
+
the access token (auth.access_token by default): it is re-read from this
|
|
37
|
+
install's own store on every client, never shared between installs."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, *, token_fn: Optional[Callable[..., str]] = None,
|
|
40
|
+
client: Optional[httpx.Client] = None,
|
|
41
|
+
sleep: Callable[[float], None] = time.sleep):
|
|
42
|
+
if token_fn is None:
|
|
43
|
+
from . import auth
|
|
44
|
+
token_fn = auth.access_token
|
|
45
|
+
self._token_fn = token_fn
|
|
46
|
+
self._token: Optional[str] = None
|
|
47
|
+
self.client = client or httpx.Client(timeout=TIMEOUT, follow_redirects=False)
|
|
48
|
+
self._own_client = client is None
|
|
49
|
+
self.sleep = sleep
|
|
50
|
+
|
|
51
|
+
def close(self) -> None:
|
|
52
|
+
if self._own_client:
|
|
53
|
+
try:
|
|
54
|
+
self.client.close()
|
|
55
|
+
except Exception:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
def __enter__(self):
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
def __exit__(self, *a):
|
|
62
|
+
self.close()
|
|
63
|
+
|
|
64
|
+
def _headers(self, force: bool = False) -> dict:
|
|
65
|
+
if self._token is None or force:
|
|
66
|
+
try:
|
|
67
|
+
self._token = self._token_fn(force_refresh=force) if force else self._token_fn()
|
|
68
|
+
except TypeError:
|
|
69
|
+
self._token = self._token_fn()
|
|
70
|
+
return {"Authorization": f"Bearer {self._token}"}
|
|
71
|
+
|
|
72
|
+
# ── requests ──
|
|
73
|
+
def get(self, api: str, path: str, params: Any = None, *, raw: bool = False) -> Any:
|
|
74
|
+
return self._request("GET", api, path, params=params, raw=raw)
|
|
75
|
+
|
|
76
|
+
def post(self, api: str, path: str, body: dict) -> Any:
|
|
77
|
+
return self._request("POST", api, path, json=body)
|
|
78
|
+
|
|
79
|
+
def patch(self, api: str, path: str, body: dict) -> Any:
|
|
80
|
+
return self._request("PATCH", api, path, json=body)
|
|
81
|
+
|
|
82
|
+
def _request(self, method: str, api: str, path: str, *, params: Any = None,
|
|
83
|
+
json: Optional[dict] = None, raw: bool = False) -> Any:
|
|
84
|
+
url = f"{BASES[api]}/{path.lstrip('/')}"
|
|
85
|
+
refreshed = False
|
|
86
|
+
attempt = 0
|
|
87
|
+
while True:
|
|
88
|
+
try:
|
|
89
|
+
resp = self.client.request(method, url, params=params, json=json,
|
|
90
|
+
headers=self._headers())
|
|
91
|
+
except httpx.HTTPError as e:
|
|
92
|
+
# A write may have gone through: never retried.
|
|
93
|
+
if method != "GET" or attempt >= MAX_RETRIES:
|
|
94
|
+
raise GoogleError(Reason.UNREACHABLE, type(e).__name__)
|
|
95
|
+
attempt += 1
|
|
96
|
+
self.sleep(min(2 ** attempt, MAX_WAIT))
|
|
97
|
+
continue
|
|
98
|
+
code = resp.status_code
|
|
99
|
+
if code == 401 and not refreshed:
|
|
100
|
+
refreshed = True # reload/refresh this install's token once
|
|
101
|
+
self._headers(force=True)
|
|
102
|
+
continue
|
|
103
|
+
if code == 401:
|
|
104
|
+
raise GoogleError(Reason.EXPIRED, "401")
|
|
105
|
+
if code == 429 or code >= 500:
|
|
106
|
+
if method != "GET" or attempt >= MAX_RETRIES:
|
|
107
|
+
raise GoogleError(Reason.RATE_LIMITED if code == 429 else Reason.UNREACHABLE,
|
|
108
|
+
f"HTTP {code}")
|
|
109
|
+
attempt += 1
|
|
110
|
+
self.sleep(_retry_after(resp) if code == 429 else min(2 ** attempt, MAX_WAIT))
|
|
111
|
+
continue
|
|
112
|
+
if code == 403:
|
|
113
|
+
msg = _message(resp).lower()
|
|
114
|
+
if "scope" in msg or "insufficient" in msg:
|
|
115
|
+
raise GoogleError(Reason.MISSING_SCOPE, "403 scope")
|
|
116
|
+
raise GoogleError(Reason.NO_ACCESS, "403")
|
|
117
|
+
if code in (404, 410):
|
|
118
|
+
raise GoogleError(Reason.NOT_FOUND, str(code))
|
|
119
|
+
if code == 400:
|
|
120
|
+
raise GoogleError(Reason.BAD_REQUEST, "400")
|
|
121
|
+
if code < 200 or code >= 300:
|
|
122
|
+
raise GoogleError(Reason.UNREADABLE, f"HTTP {code}")
|
|
123
|
+
if raw:
|
|
124
|
+
return resp.content
|
|
125
|
+
if not resp.content:
|
|
126
|
+
return {}
|
|
127
|
+
try:
|
|
128
|
+
body = resp.json()
|
|
129
|
+
except Exception:
|
|
130
|
+
raise GoogleError(Reason.UNREADABLE, "not json")
|
|
131
|
+
if not isinstance(body, dict):
|
|
132
|
+
raise GoogleError(Reason.UNREADABLE, "not an object")
|
|
133
|
+
return body
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _retry_after(resp) -> float:
|
|
137
|
+
try:
|
|
138
|
+
return max(0.0, min(float(resp.headers.get("Retry-After", "1")), MAX_WAIT))
|
|
139
|
+
except ValueError:
|
|
140
|
+
return 1.0
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _message(resp) -> str:
|
|
144
|
+
try:
|
|
145
|
+
b = resp.json()
|
|
146
|
+
e = b.get("error") if isinstance(b, dict) else None
|
|
147
|
+
if isinstance(e, dict):
|
|
148
|
+
return str(e.get("message") or "") + " " + str(e.get("status") or "")
|
|
149
|
+
except Exception:
|
|
150
|
+
pass
|
|
151
|
+
return ""
|