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,361 @@
1
+ """
2
+ Unified API for the Civilization Knowledge Layer.
3
+ Provides high-level operations for managing organizational knowledge.
4
+ """
5
+ from __future__ import annotations
6
+ import logging
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime
9
+ from typing import Any, Callable
10
+ from uuid import UUID
11
+
12
+ from ..schema import (
13
+ CivilizationNode, NodeType, NodeStatus,
14
+ SOP, Role, Person, Checklist, Runbook, Workflow, OrgChart,
15
+ JobDescription, ClientProfile, Policy, KnowledgeArticle,
16
+ )
17
+ from ..store import CivilizationStore
18
+ from ..ingestion.document_ingestor import DocumentIngestor
19
+ from ..ingestion.embedder import CivilizationEmbedder
20
+ from ..interview.interviewer import Interviewer, InterviewSession
21
+ from ..interview.gap_detector import GapDetector, GapReport
22
+ from ..graph.org_graph import OrgGraph
23
+ from ..graph.process_graph import ProcessGraph
24
+ from ..graph.knowledge_linker import KnowledgeLinker, KnowledgeLink
25
+ from ..versioning.version_manager import VersionManager, NodeVersion
26
+ from ..versioning.staleness_detector import StalenessDetector, StalenessReport
27
+ from ..retrieval.civilization_rag import CivilizationRAG, RAGResult, RAGConfig
28
+ from ..retrieval.context_injector import ContextInjector, InjectionConfig
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ @dataclass
34
+ class CivilizationStats:
35
+ """Statistics about the civilization knowledge base."""
36
+ total_nodes: int = 0
37
+ by_type: dict[str, int] = field(default_factory=dict)
38
+ by_status: dict[str, int] = field(default_factory=dict)
39
+ stale_count: int = 0
40
+ unowned_count: int = 0
41
+ freshness_score: float = 0.0
42
+
43
+
44
+ class CivilizationAPI:
45
+ """
46
+ High-level API for the Civilization Knowledge Layer.
47
+ Orchestrates all civilization components for a unified interface.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ org_id: str,
53
+ store: CivilizationStore | None = None,
54
+ embedder: CivilizationEmbedder | None = None,
55
+ ):
56
+ self.org_id = org_id
57
+ self.store = store or CivilizationStore()
58
+ self.embedder = embedder or CivilizationEmbedder()
59
+
60
+ # Initialize components
61
+ self.ingestor = DocumentIngestor(org_id, self.embedder, store=self.store)
62
+ self.rag = CivilizationRAG(org_id, self.store, self.embedder)
63
+ self.context_injector = ContextInjector()
64
+ self.interviewer = Interviewer(org_id, self.store)
65
+ self.gap_detector = GapDetector(org_id, self.store)
66
+ self.version_manager = VersionManager(org_id, self.store)
67
+ self.staleness_detector = StalenessDetector(org_id)
68
+ self.knowledge_linker = KnowledgeLinker(org_id, self.store)
69
+
70
+ self._nodes_cache: dict[UUID, CivilizationNode] = {}
71
+
72
+ # ─── Node CRUD Operations ────────────────────────────────────────────────
73
+
74
+ async def create_node(
75
+ self,
76
+ node: CivilizationNode,
77
+ create_version: bool = True,
78
+ ) -> CivilizationNode:
79
+ """Create a new civilization node."""
80
+ # Generate embedding if not present
81
+ if not node.embedding:
82
+ text = self._get_node_text(node)
83
+ node.embedding = await self.embedder.embed(text)
84
+
85
+ # Save to store
86
+ saved = await self.store.save(node)
87
+
88
+ # Create initial version
89
+ if create_version:
90
+ self.version_manager.create_version(
91
+ saved,
92
+ change_summary="Initial creation",
93
+ )
94
+
95
+ self._nodes_cache[saved.id] = saved
96
+ return saved
97
+
98
+ async def get_node(self, node_id: UUID) -> CivilizationNode | None:
99
+ """Get a node by ID."""
100
+ if node_id in self._nodes_cache:
101
+ return self._nodes_cache[node_id]
102
+
103
+ node = await self.store.get(node_id, self.org_id)
104
+ if node:
105
+ self._nodes_cache[node_id] = node
106
+ return node
107
+
108
+ async def update_node(
109
+ self,
110
+ node: CivilizationNode,
111
+ change_summary: str | None = None,
112
+ bump_type: str = "patch",
113
+ ) -> CivilizationNode:
114
+ """Update an existing node."""
115
+ node.updated_at = datetime.utcnow()
116
+
117
+ # Regenerate embedding
118
+ text = self._get_node_text(node)
119
+ node.embedding = await self.embedder.embed(text)
120
+
121
+ # Create new version if changed
122
+ if self.version_manager.has_changes(node):
123
+ self.version_manager.create_version(
124
+ node,
125
+ bump_type=bump_type,
126
+ change_summary=change_summary,
127
+ )
128
+ node.version = self.version_manager.get_current_version(node.id)
129
+
130
+ # Save
131
+ saved = await self.store.save(node)
132
+ self._nodes_cache[saved.id] = saved
133
+ return saved
134
+
135
+ async def delete_node(self, node_id: UUID) -> bool:
136
+ """Delete a node."""
137
+ success = await self.store.delete(node_id, self.org_id)
138
+ if success and node_id in self._nodes_cache:
139
+ del self._nodes_cache[node_id]
140
+ return success
141
+
142
+ async def list_nodes(
143
+ self,
144
+ node_type: NodeType | None = None,
145
+ limit: int = 100,
146
+ ) -> list[CivilizationNode]:
147
+ """List nodes, optionally filtered by type."""
148
+ if node_type:
149
+ return await self.store.list_by_type(self.org_id, node_type, limit)
150
+ return await self.store.list_all(self.org_id, limit)
151
+
152
+ # ─── Search & Retrieval ──────────────────────────────────────────────────
153
+
154
+ async def search(
155
+ self,
156
+ query: str,
157
+ top_k: int = 5,
158
+ node_types: list[NodeType] | None = None,
159
+ ) -> RAGResult:
160
+ """Search for relevant nodes."""
161
+ config = RAGConfig(
162
+ top_k=top_k,
163
+ node_type_filters=node_types,
164
+ )
165
+ return await self.rag.query(query, config)
166
+
167
+ async def get_context_for_task(
168
+ self,
169
+ task_description: str,
170
+ max_tokens: int = 2000,
171
+ ) -> str:
172
+ """Get formatted context for an agent task."""
173
+ result = await self.search(task_description, top_k=5)
174
+ nodes = [rn.node for rn in result.retrieved_nodes]
175
+
176
+ config = InjectionConfig(max_tokens=max_tokens)
177
+ injection = self.context_injector.inject(nodes, config)
178
+ return injection.context
179
+
180
+ async def find_sop(self, task: str) -> list[SOP]:
181
+ """Find SOPs relevant to a task."""
182
+ result = await self.rag.get_sop_for_task(task)
183
+ return [rn.node for rn in result.retrieved_nodes if isinstance(rn.node, SOP)]
184
+
185
+ async def find_runbook(self, incident: str) -> list[Runbook]:
186
+ """Find runbooks for an incident."""
187
+ result = await self.rag.get_runbook_for_incident(incident)
188
+ return [rn.node for rn in result.retrieved_nodes if isinstance(rn.node, Runbook)]
189
+
190
+ # ─── Ingestion ───────────────────────────────────────────────────────────
191
+
192
+ async def ingest_document(
193
+ self,
194
+ content: str | bytes,
195
+ filename: str | None = None,
196
+ source: str | None = None,
197
+ source_url: str | None = None,
198
+ node_type: NodeType | None = None,
199
+ ) -> CivilizationNode | None:
200
+ """Ingest a document into the knowledge base."""
201
+ result = await self.ingestor.ingest(
202
+ content=content,
203
+ filename=filename,
204
+ source=source,
205
+ source_url=source_url,
206
+ node_type_hint=node_type,
207
+ )
208
+ if result.success and result.node_id:
209
+ return await self.get_node(result.node_id)
210
+ return None
211
+
212
+ # ─── Interviews ──────────────────────────────────────────────────────────
213
+
214
+ def start_interview(
215
+ self,
216
+ node_type: NodeType,
217
+ subject: str,
218
+ ) -> InterviewSession:
219
+ """Start a knowledge elicitation interview."""
220
+ return self.interviewer.start_session(node_type, subject)
221
+
222
+ def submit_interview_response(
223
+ self,
224
+ session_id: UUID,
225
+ response: str,
226
+ ) -> dict:
227
+ """Submit a response to an interview question."""
228
+ return self.interviewer.submit_response(session_id, response)
229
+
230
+ async def complete_interview(
231
+ self,
232
+ session_id: UUID,
233
+ ) -> CivilizationNode | None:
234
+ """Complete an interview and create the node."""
235
+ node = self.interviewer.compile_to_node(session_id)
236
+ if node:
237
+ return await self.create_node(node)
238
+ return None
239
+
240
+ # ─── Analysis & Health ───────────────────────────────────────────────────
241
+
242
+ async def analyze_gaps(self) -> GapReport:
243
+ """Analyze knowledge base for gaps."""
244
+ nodes = await self.list_nodes(limit=1000)
245
+ return await self.gap_detector.analyze(nodes)
246
+
247
+ async def check_staleness(self) -> StalenessReport:
248
+ """Check for stale content."""
249
+ nodes = await self.list_nodes(limit=1000)
250
+ return self.staleness_detector.check_nodes(nodes)
251
+
252
+ async def get_stats(self) -> CivilizationStats:
253
+ """Get knowledge base statistics."""
254
+ stats = CivilizationStats()
255
+
256
+ # Count by type
257
+ stats.by_type = await self.store.count_by_type(self.org_id)
258
+ stats.total_nodes = sum(stats.by_type.values())
259
+
260
+ # Check staleness
261
+ nodes = await self.list_nodes(limit=1000)
262
+ staleness = self.staleness_detector.check_nodes(nodes)
263
+ stats.stale_count = staleness.stale_count
264
+ stats.freshness_score = staleness.freshness_score
265
+
266
+ # Count unowned
267
+ stats.unowned_count = sum(1 for n in nodes if not n.owner_id)
268
+
269
+ # Count by status
270
+ for node in nodes:
271
+ status = node.status.value
272
+ stats.by_status[status] = stats.by_status.get(status, 0) + 1
273
+
274
+ return stats
275
+
276
+ # ─── Graphs ──────────────────────────────────────────────────────────────
277
+
278
+ async def build_org_graph(self) -> OrgGraph:
279
+ """Build organizational graph from person nodes."""
280
+ people = await self.list_nodes(NodeType.PERSON, limit=500)
281
+ return OrgGraph.from_people(self.org_id, [p for p in people if isinstance(p, Person)])
282
+
283
+ async def build_process_graph(self, sop_id: UUID) -> ProcessGraph | None:
284
+ """Build process graph from an SOP."""
285
+ node = await self.get_node(sop_id)
286
+ if isinstance(node, SOP):
287
+ return ProcessGraph.from_sop(node)
288
+ return None
289
+
290
+ async def detect_links(self) -> list[KnowledgeLink]:
291
+ """Auto-detect links between nodes."""
292
+ nodes = await self.list_nodes(limit=1000)
293
+ links = self.knowledge_linker.detect_links(nodes)
294
+ self.knowledge_linker.bulk_add_links(links)
295
+ return links
296
+
297
+ # ─── Versioning ──────────────────────────────────────────────────────────
298
+
299
+ def get_node_history(self, node_id: UUID) -> list[dict]:
300
+ """Get version history for a node."""
301
+ return self.version_manager.get_version_timeline(node_id)
302
+
303
+ async def restore_version(
304
+ self,
305
+ node_id: UUID,
306
+ version: str,
307
+ ) -> CivilizationNode | None:
308
+ """Restore a node to a previous version."""
309
+ node = self.version_manager.restore_version(node_id, version)
310
+ if node:
311
+ return await self.update_node(node, f"Restored to version {version}")
312
+ return None
313
+
314
+ # ─── Helpers ─────────────────────────────────────────────────────────────
315
+
316
+ def _get_node_text(self, node: CivilizationNode) -> str:
317
+ """Extract text for embedding from a node."""
318
+ parts = [node.title]
319
+
320
+ node_dict = node.model_dump()
321
+
322
+ # Add type-specific content
323
+ if node.node_type == NodeType.SOP:
324
+ parts.append(node_dict.get("purpose", ""))
325
+ parts.append(node_dict.get("scope", ""))
326
+ for step in node_dict.get("steps", []):
327
+ parts.append(step.get("title", ""))
328
+ parts.append(step.get("description", ""))
329
+
330
+ elif node.node_type == NodeType.ROLE:
331
+ parts.extend(node_dict.get("responsibilities", []))
332
+ parts.extend(node_dict.get("accountabilities", []))
333
+
334
+ elif node.node_type == NodeType.JOB_DESCRIPTION:
335
+ parts.append(node_dict.get("summary", ""))
336
+ parts.extend(node_dict.get("responsibilities", []))
337
+
338
+ elif node.node_type == NodeType.CHECKLIST:
339
+ parts.append(node_dict.get("purpose", ""))
340
+ for item in node_dict.get("items", []):
341
+ parts.append(item.get("description", ""))
342
+
343
+ elif node.node_type == NodeType.RUNBOOK:
344
+ parts.append(node_dict.get("scenario", ""))
345
+ for step in node_dict.get("steps", []):
346
+ parts.append(step.get("action", ""))
347
+
348
+ elif node.node_type == NodeType.POLICY:
349
+ parts.append(node_dict.get("summary", ""))
350
+ parts.append(node_dict.get("full_text", "")[:1000])
351
+
352
+ elif node.node_type == NodeType.KNOWLEDGE_ARTICLE:
353
+ parts.append(node_dict.get("summary", ""))
354
+ parts.append(node_dict.get("content", "")[:1500])
355
+
356
+ return " ".join(filter(None, parts))[:3000]
357
+
358
+ def clear_cache(self) -> None:
359
+ """Clear internal caches."""
360
+ self._nodes_cache.clear()
361
+ self.embedder.clear_cache()
@@ -0,0 +1,19 @@
1
+ """
2
+ Graph module for Civilization Knowledge Layer.
3
+ Handles organizational structure, process flows, and knowledge relationships.
4
+ """
5
+ from .org_graph import OrgGraph, OrgNode, OrgEdge
6
+ from .process_graph import ProcessGraph, ProcessNode, ProcessEdge
7
+ from .knowledge_linker import KnowledgeLinker, LinkType, KnowledgeLink
8
+
9
+ __all__ = [
10
+ "OrgGraph",
11
+ "OrgNode",
12
+ "OrgEdge",
13
+ "ProcessGraph",
14
+ "ProcessNode",
15
+ "ProcessEdge",
16
+ "KnowledgeLinker",
17
+ "LinkType",
18
+ "KnowledgeLink",
19
+ ]