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,112 @@
1
+ """
2
+ Watchers (``agent_start``), owner-only.
3
+
4
+ Watchers are ON again in rc5, inside one constraint (Vaughn's spec):
5
+
6
+ - they only ever message their OWNER (the owner Telegram chat(s) from
7
+ ``chat_scope.owner_ids``; never TELEGRAM_CHAT_ID from the environment, never
8
+ anyone else);
9
+ - in ONE fixed format, one line per alert: ``<who>: <what> - <one line>``
10
+ (``format_alert``). Markdown, line breaks and every web address are
11
+ removed, link previews are off: a watcher alert never opens a link and
12
+ never makes Telegram open one;
13
+ - they never act: anything other than this one-line alert to the owner
14
+ (a reply, a forward, a post, a fetch of something they saw) is a normal
15
+ request that waits in the approval queue for Send.
16
+
17
+ Starting them is itself an ``ask``-tier tool call: the card says exactly what
18
+ the watchers will do (``START_CARD``) and nothing runs until the owner
19
+ presses Send. Because every later message has this fixed shape and goes only
20
+ to the owner, what they would send CAN be shown up front, which is why
21
+ agent_start is no longer tier "never" (tools/catalog.py).
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import logging
26
+ import re
27
+ from typing import Callable, Dict, Iterable, List, Optional
28
+
29
+ log = logging.getLogger("hyperclaw.team.watchers")
30
+
31
+ LINE_MAX = 160
32
+ _URL = re.compile(r"(?i)\b(?:https?://|ftp://|www\.)\S+|\b[\w.-]+\.(?:com|net|org|io|co|ai|app|"
33
+ r"dev|ly|gl|me|info|biz|us|uk)(?:/\S*)?\b")
34
+ _MARK = re.compile(r"[*_`~<>#\[\]()|]+")
35
+ _SPACE = re.compile(r"\s+")
36
+
37
+ START_CARD = ("Start background watchers. They read only what they watch (unread mail "
38
+ "subjects and senders, prices, news headlines), and they only ever message YOU, "
39
+ "one line each: who, what, one line. They never open links and never reply, "
40
+ "post or act; anything else waits for your Send.")
41
+
42
+ WHO = {"email_monitor": "Email watcher", "market_monitor": "Market watcher",
43
+ "news_monitor": "News watcher"}
44
+
45
+
46
+ def clean(text: str, limit: int = LINE_MAX) -> str:
47
+ """One plain line: no addresses, no markdown, no line breaks."""
48
+ t = _URL.sub("\x00", str(text or ""))
49
+ t = _MARK.sub(" ", t).replace("\x00", "[link removed]")
50
+ t = _SPACE.sub(" ", t).strip()
51
+ if len(t) > limit:
52
+ t = t[:limit - 1].rstrip() + "…"
53
+ return t
54
+
55
+
56
+ def format_alert(who: str, what: str, line: str) -> str:
57
+ """The one fixed format: '<who>: <what> - <one line>'."""
58
+ return f"{clean(who, 40)}: {clean(what, 40)} - {clean(line)}"
59
+
60
+
61
+ def alert_lines(agent_name: str, result: dict) -> List[str]:
62
+ """The fixed-format lines for one watcher result (at most 3)."""
63
+ who = WHO.get(agent_name, agent_name.replace("_", " ").capitalize())
64
+ out: List[str] = []
65
+ for e in (result.get("emails") or [])[:3]:
66
+ out.append(format_alert(who, "important email",
67
+ f"from {e.get('sender', '?')}: {e.get('subject', '')}"))
68
+ for m in (result.get("moves") or [])[:3]:
69
+ out.append(format_alert(who, f"{m.get('symbol', '?')} moved",
70
+ f"{m.get('move', '')} to {m.get('price', '')}"))
71
+ for n in (result.get("news") or [])[:3]:
72
+ out.append(format_alert(who, "news", n.get("title", "")))
73
+ if not out:
74
+ out.append(format_alert(who, "alert", "something needs your attention (no detail)"))
75
+ return out
76
+
77
+
78
+ def owner_chats() -> List[int]:
79
+ from hyperclaw.chat_scope import owner_ids
80
+ out = []
81
+ for c in sorted(owner_ids("telegram")):
82
+ try:
83
+ out.append(int(c))
84
+ except (TypeError, ValueError):
85
+ continue
86
+ return out
87
+
88
+
89
+ def send_owner_alert(agent_name: str, result: dict, *,
90
+ send: Optional[Callable[[int, str], bool]] = None,
91
+ chats: Optional[Iterable[int]] = None) -> List[int]:
92
+ """Send the fixed-format lines to the owner chat(s) only. Returns the
93
+ chats reached. Plain text, link previews off (brief.service.telegram_send)."""
94
+ if send is None:
95
+ from hyperclaw.brief.service import telegram_send as send
96
+ text = "\n".join(alert_lines(agent_name, result))
97
+ reached = []
98
+ for chat in (owner_chats() if chats is None else list(chats)):
99
+ try:
100
+ if send(int(chat), text):
101
+ reached.append(int(chat))
102
+ except Exception as e:
103
+ log.warning("watcher alert not delivered (%s)", type(e).__name__)
104
+ if not reached:
105
+ log.info("watcher alert: no owner chat reached")
106
+ return reached
107
+
108
+
109
+ async def owner_alert_callback(agent_name: str, result: Dict) -> None:
110
+ """The AgentManager alert callback (agents/background_monitor.py)."""
111
+ import asyncio
112
+ await asyncio.to_thread(send_owner_alert, agent_name, result)
@@ -0,0 +1,17 @@
1
+ """Remote team onboarding (a team member setting up their own install;
2
+ not the team of agents in hyperclaw.team) (docs/TEAM-ONBOARDING.md).
3
+
4
+ A team member sets up HyperAgents alone, from one 1Password item:
5
+
6
+ - ``item`` the versioned item format and its parser (a Hyper-signed team
7
+ profile plus their personal secrets);
8
+ - ``secrets`` the OS secret store (macOS keychain, Linux Secret Service),
9
+ else an owner-only 0600 file;
10
+ - ``profile`` the verified team profile on this install (read by
11
+ ``hyperclaw.managed_config`` as the install's managed settings);
12
+ - ``slack_oauth`` / ``google`` / ``checks`` sign-ins and the minimal
13
+ authenticated call that proves each connection;
14
+ - ``onboard`` the resumable step runner behind ``hyperclaw onboard --team``;
15
+ - ``service`` the one background service at login (Telegram + scheduler);
16
+ - ``selfcheck`` the redacted report for ``hyperclaw self-check``.
17
+ """
@@ -0,0 +1,208 @@
1
+ """One minimal authenticated call per connected piece, for the "✓ Connected"
2
+ lines of ``hyperclaw onboard --team`` and for ``hyperclaw self-check``.
3
+
4
+ Every check returns a ``Check`` with a plain line: ``✓ Connected: <piece> ...``
5
+ or ``✗ <piece>: <reason>. Fix: <what to do>``. No line ever holds a key, a
6
+ token, an email address or message text. Calls are read-only.
7
+
8
+ - model: ``GET /v1/models/<id>`` on the Anthropic API (no tokens used);
9
+ - Telegram: ``getMe`` (and the owner chat is paired);
10
+ - Google: ``tokeninfo`` for the stored access (refreshed when needed);
11
+ - Slack: ``auth.test`` (hyperclaw.teamsetup.slack_oauth);
12
+ - GitLab: ``personal_access_tokens/self`` + ``user`` (hyperclaw.digest.gitlab).
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ import os
18
+ from dataclasses import dataclass
19
+ from typing import Optional
20
+
21
+ import httpx
22
+
23
+ log = logging.getLogger("hyperclaw.teamsetup.checks")
24
+
25
+ TIMEOUT = 15.0
26
+ ANTHROPIC_API = "https://api.anthropic.com"
27
+ ANTHROPIC_VERSION = "2023-06-01"
28
+ GOOGLE_TOKENINFO = "https://oauth2.googleapis.com/tokeninfo"
29
+ RERUN = "hyperclaw onboard --team --step {step}"
30
+
31
+
32
+ @dataclass
33
+ class Check:
34
+ piece: str
35
+ ok: bool
36
+ line: str
37
+ step: str = ""
38
+ code: str = ""
39
+
40
+
41
+ def ok(piece: str, detail: str = "", step: str = "") -> Check:
42
+ return Check(piece, True, f"✓ Connected: {piece}" + (f" ({detail})" if detail else ""), step)
43
+
44
+
45
+ def fail(piece: str, reason: str, fix: str, step: str = "", code: str = "") -> Check:
46
+ reason = reason[:1].upper() + reason[1:]
47
+ return Check(piece, False, f"✗ {piece}: {reason.rstrip('.')}. Fix: {fix}", step, code)
48
+
49
+
50
+ def _client(http):
51
+ return http or httpx.Client(timeout=TIMEOUT, follow_redirects=False)
52
+
53
+
54
+ # ── model ─────────────────────────────────────────────────────────────────
55
+
56
+ def model_label(provider: str, model: str) -> str:
57
+ from hyperclaw.model_policy import pretty_model_name
58
+ from hyperclaw import managed_config
59
+ name = pretty_model_name(provider, model)
60
+ return f"{name}, via Hyper" if managed_config.hyper_issued(provider) else name
61
+
62
+
63
+ def check_model(provider: str, model: str, key: str = "", *,
64
+ http: Optional[httpx.Client] = None) -> Check:
65
+ label = model_label(provider, model)
66
+ step = "item"
67
+ if provider != "anthropic":
68
+ return fail(label, "this install's model isn't one onboarding can check",
69
+ "tell Vaughn", step, "unsupported")
70
+ key = key or os.environ.get("ANTHROPIC_API_KEY", "")
71
+ if not key:
72
+ return fail(label, "no model key is stored", RERUN.format(step="item"), step, "no_key")
73
+ base = (os.environ.get("ANTHROPIC_BASE_URL", "").strip() or ANTHROPIC_API).rstrip("/")
74
+ c = _client(http)
75
+ try:
76
+ r = c.get(f"{base}/v1/models/{model}",
77
+ headers={"x-api-key": key, "anthropic-version": ANTHROPIC_VERSION})
78
+ except httpx.HTTPError as e:
79
+ log.warning("model check: network error (%s)", type(e).__name__)
80
+ return fail(label, "the model service couldn't be reached",
81
+ "check your internet connection, then run hyperclaw self-check", step,
82
+ "unreachable")
83
+ finally:
84
+ if http is None:
85
+ c.close()
86
+ if r.status_code == 200:
87
+ return ok(label, "", step)
88
+ log.warning("model check: HTTP %s", r.status_code)
89
+ if r.status_code in (401, 403):
90
+ return fail(label, "the model key in your 1Password item was not accepted",
91
+ "tell Vaughn; he will send a new item", step, "auth")
92
+ if r.status_code == 404:
93
+ return fail(label, f"your key works, but {model} isn't available to it",
94
+ "tell Vaughn", step, "model")
95
+ return fail(label, "the model service isn't available right now",
96
+ "try again in a few minutes with hyperclaw self-check", step, "unavailable")
97
+
98
+
99
+ # ── Telegram ──────────────────────────────────────────────────────────────
100
+
101
+ def check_telegram(*, client=None) -> Check:
102
+ from hyperclaw import telegram_pairing as tp
103
+ step = "telegram"
104
+ token = os.environ.get("TELEGRAM_BOT_TOKEN", "").strip()
105
+ owner = os.environ.get("TELEGRAM_OWNER_CHAT_ID", "").strip()
106
+ if not token:
107
+ return fail("Telegram", "no bot is connected", RERUN.format(step=step), step,
108
+ "not_connected")
109
+ name = tp.bot_username(token, client=client)
110
+ if not name:
111
+ return fail("Telegram", "Telegram didn't accept the bot token (or couldn't be reached)",
112
+ RERUN.format(step=step), step, "token")
113
+ if not owner:
114
+ return fail("Telegram", f"the bot @{name} works but isn't paired to your chat yet",
115
+ RERUN.format(step=step), step, "unpaired")
116
+ return ok("Telegram", f"@{name}, paired to your chat", step)
117
+
118
+
119
+ # ── Google ──────────────────────────────────────────────────────────────
120
+
121
+ GOOGLE_NAMES = "Gmail, Calendar, Meet, Drive, Docs, Sheets and Slides"
122
+
123
+
124
+ def check_google(*, http: Optional[httpx.Client] = None) -> Check:
125
+ """The Workspace sign-in (hyperclaw.google_workspace): refresh if needed,
126
+ then one tokeninfo call; the granted scopes must be ones we asked for."""
127
+ from hyperclaw.google_workspace import auth as ga
128
+ from hyperclaw.google_workspace.errors import GoogleError
129
+ step = "google"
130
+ if not ga.connected():
131
+ return fail("Google", "you haven't signed in to Google yet", RERUN.format(step=step),
132
+ step, "not_connected")
133
+ try:
134
+ tok = ga.access_token(http=http)
135
+ except GoogleError as e:
136
+ return fail("Google", e.text.split(". ")[0], RERUN.format(step=step), step,
137
+ e.reason.value)
138
+ c = _client(http)
139
+ try:
140
+ r = c.post(GOOGLE_TOKENINFO, data={"access_token": tok})
141
+ except httpx.HTTPError:
142
+ return fail("Google", "Google couldn't be reached",
143
+ "check your internet connection, then run hyperclaw self-check", step,
144
+ "unreachable")
145
+ finally:
146
+ if http is None:
147
+ c.close()
148
+ if r.status_code != 200:
149
+ return fail("Google", "Google didn't accept the stored sign-in",
150
+ RERUN.format(step=step), step, "rejected")
151
+ try:
152
+ granted = ga.check_scopes(str((r.json() or {}).get("scope") or ""))
153
+ except (GoogleError, ValueError) as e:
154
+ return fail("Google", getattr(e, "text", "Google's answer couldn't be read").split(". ")[0],
155
+ RERUN.format(step=step), step, "scopes")
156
+ missing = [s.rsplit("/", 1)[-1] for s in ga.SCOPES if s not in granted]
157
+ detail = GOOGLE_NAMES + ("; not granted: " + ", ".join(missing) if missing else "")
158
+ return ok("Google", detail, step)
159
+
160
+
161
+ # ── Slack ─────────────────────────────────────────────────────────────────
162
+
163
+ def check_slack(cfg: dict, *, http: Optional[httpx.Client] = None) -> Check:
164
+ from hyperclaw.teamsetup import slack_oauth as so
165
+ step = "slack"
166
+ try:
167
+ so.expected_workspace(cfg)
168
+ except so.SlackAuthError as e:
169
+ return fail("Slack", e.text, e.fix, step, e.code)
170
+ if not so.connected():
171
+ return fail("Slack", "you haven't signed in to Slack yet", RERUN.format(step=step),
172
+ step, "not_connected")
173
+ try:
174
+ info = so.verify(cfg, http=http)
175
+ except so.SlackAuthError as e:
176
+ if e.code == "expired":
177
+ return fail("Slack", "your Slack sign-in has expired or was removed",
178
+ "sign in again: " + RERUN.format(step=step), step, e.code)
179
+ return fail("Slack", e.text, e.fix, step, e.code)
180
+ return ok("Slack", f"workspace {info['team']}; posts only when you press Send" if info["team"]
181
+ else "posts only when you press Send",
182
+ step)
183
+
184
+
185
+ # ── GitLab ────────────────────────────────────────────────────────────────
186
+
187
+ def check_gitlab(*, client=None) -> Check:
188
+ from hyperclaw.digest import state, tokens
189
+ from hyperclaw.digest.errors import DigestError
190
+ from hyperclaw.digest.gitlab import GitLabCollector
191
+ step = "gitlab"
192
+ meta = state.connections().get("gitlab") or {}
193
+ try:
194
+ token = tokens.store(meta.get("store") or "").get("gitlab")
195
+ except DigestError:
196
+ token = ""
197
+ if not token:
198
+ return fail("GitLab", "GitLab isn't connected (optional)", RERUN.format(step=step),
199
+ step, "not_connected")
200
+ col = GitLabCollector(token, url=meta.get("url") or "", client=client)
201
+ try:
202
+ info = col.verify()
203
+ except DigestError as e:
204
+ return fail("GitLab", e.text, e.fix or RERUN.format(step=step), step, e.reason.value)
205
+ finally:
206
+ if client is None:
207
+ col.close()
208
+ return ok("GitLab", f"{info.get('account', '')}, read_api", step)