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
agents/learning.py ADDED
@@ -0,0 +1,460 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Assistant Self-Improvement System — Learn from successes and failures.
4
+ Tracks patterns, updates instincts, reflects on performance.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ import sqlite3
11
+ from pathlib import Path
12
+ from datetime import datetime, timedelta
13
+ from typing import Dict, List, Optional, Any
14
+ from collections import defaultdict
15
+
16
+ from hyperclaw.taint import training_sink
17
+
18
+ # Use user's ~/.hyperclaw directory, not package location
19
+ HYPERCLAW_ROOT = Path(os.environ.get("HYPERCLAW_ROOT", Path.home() / ".hyperclaw"))
20
+ LEARNING_DB = HYPERCLAW_ROOT / "memory" / "learning.db"
21
+ INSTINCTS_FILE = HYPERCLAW_ROOT / "memory" / "instincts.md"
22
+
23
+
24
+ def init_db():
25
+ """Initialize the learning database."""
26
+ LEARNING_DB.parent.mkdir(parents=True, exist_ok=True)
27
+ conn = sqlite3.connect(str(LEARNING_DB))
28
+ conn.executescript("""
29
+ CREATE TABLE IF NOT EXISTS interactions (
30
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
31
+ timestamp TEXT NOT NULL,
32
+ action_type TEXT NOT NULL,
33
+ action_detail TEXT,
34
+ outcome TEXT NOT NULL,
35
+ success INTEGER NOT NULL,
36
+ feedback TEXT,
37
+ context TEXT,
38
+ learned TEXT
39
+ );
40
+
41
+ CREATE TABLE IF NOT EXISTS patterns (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ pattern_type TEXT NOT NULL,
44
+ description TEXT NOT NULL,
45
+ frequency INTEGER DEFAULT 1,
46
+ success_rate REAL DEFAULT 0.0,
47
+ last_seen TEXT,
48
+ action TEXT
49
+ );
50
+
51
+ CREATE TABLE IF NOT EXISTS reflections (
52
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
53
+ timestamp TEXT NOT NULL,
54
+ period TEXT NOT NULL,
55
+ summary TEXT NOT NULL,
56
+ insights TEXT,
57
+ improvements TEXT
58
+ );
59
+
60
+ CREATE INDEX IF NOT EXISTS idx_interactions_type ON interactions(action_type);
61
+ CREATE INDEX IF NOT EXISTS idx_interactions_outcome ON interactions(outcome);
62
+ CREATE INDEX IF NOT EXISTS idx_patterns_type ON patterns(pattern_type);
63
+ """)
64
+ conn.commit()
65
+ conn.close()
66
+
67
+
68
+ # Never let a storage problem kill the import — callers can retry lazily.
69
+ try:
70
+ init_db()
71
+ except sqlite3.OperationalError as _e:
72
+ import logging
73
+ logging.getLogger(__name__).warning("learning db unavailable: %s", _e)
74
+
75
+
76
+ @training_sink("learning.log_interaction")
77
+ def log_interaction(
78
+ action_type: str,
79
+ action_detail: str,
80
+ outcome: str,
81
+ success: bool,
82
+ feedback: str = None,
83
+ context: Dict = None
84
+ ) -> str:
85
+ """Log an interaction for learning."""
86
+ conn = sqlite3.connect(str(LEARNING_DB))
87
+
88
+ conn.execute("""
89
+ INSERT INTO interactions (timestamp, action_type, action_detail, outcome, success, feedback, context)
90
+ VALUES (?, ?, ?, ?, ?, ?, ?)
91
+ """, (
92
+ datetime.now().isoformat(),
93
+ action_type,
94
+ action_detail,
95
+ outcome,
96
+ 1 if success else 0,
97
+ feedback,
98
+ json.dumps(context) if context else None
99
+ ))
100
+
101
+ conn.commit()
102
+ conn.close()
103
+
104
+ # Update patterns
105
+ _update_patterns(action_type, success)
106
+
107
+ # If failure, try to learn from it
108
+ if not success and feedback:
109
+ return _learn_from_failure(action_type, action_detail, outcome, feedback)
110
+
111
+ return f"Logged: {action_type} - {'success' if success else 'failure'}"
112
+
113
+
114
+ @training_sink("learning.update_patterns")
115
+ def _update_patterns(action_type: str, success: bool):
116
+ """Update pattern statistics."""
117
+ conn = sqlite3.connect(str(LEARNING_DB))
118
+
119
+ # Check if pattern exists
120
+ existing = conn.execute(
121
+ "SELECT id, frequency, success_rate FROM patterns WHERE pattern_type = ?",
122
+ (action_type,)
123
+ ).fetchone()
124
+
125
+ if existing:
126
+ pattern_id, freq, rate = existing
127
+ new_freq = freq + 1
128
+ # Running average
129
+ new_rate = ((rate * freq) + (1 if success else 0)) / new_freq
130
+ conn.execute("""
131
+ UPDATE patterns SET frequency = ?, success_rate = ?, last_seen = ?
132
+ WHERE id = ?
133
+ """, (new_freq, new_rate, datetime.now().isoformat(), pattern_id))
134
+ else:
135
+ conn.execute("""
136
+ INSERT INTO patterns (pattern_type, description, frequency, success_rate, last_seen)
137
+ VALUES (?, ?, 1, ?, ?)
138
+ """, (action_type, f"Pattern for {action_type}", 1.0 if success else 0.0, datetime.now().isoformat()))
139
+
140
+ conn.commit()
141
+ conn.close()
142
+
143
+
144
+ @training_sink("learning.learn_from_failure")
145
+ def _learn_from_failure(action_type: str, detail: str, outcome: str, feedback: str) -> str:
146
+ """Extract learning from a failure."""
147
+ # Simple learning extraction
148
+ learning = f"When {action_type}: {detail[:50]}... resulted in {outcome}. User feedback: {feedback}"
149
+
150
+ # Update the interaction with the learning
151
+ conn = sqlite3.connect(str(LEARNING_DB))
152
+ conn.execute("""
153
+ UPDATE interactions SET learned = ?
154
+ WHERE id = (SELECT id FROM interactions WHERE action_type = ? AND action_detail = ?
155
+ ORDER BY timestamp DESC LIMIT 1)
156
+ """, (learning, action_type, detail))
157
+ conn.commit()
158
+ conn.close()
159
+
160
+ # Check if this should become an instinct
161
+ _maybe_create_instinct(action_type, feedback)
162
+
163
+ return f"Learned: {learning[:100]}..."
164
+
165
+
166
+ def _maybe_create_instinct(action_type: str, feedback: str):
167
+ """Check if a pattern should become an instinct."""
168
+ conn = sqlite3.connect(str(LEARNING_DB))
169
+
170
+ # Count recent failures of this type
171
+ recent_failures = conn.execute("""
172
+ SELECT COUNT(*) FROM interactions
173
+ WHERE action_type = ? AND success = 0
174
+ AND timestamp > datetime('now', '-7 days')
175
+ """, (action_type,)).fetchone()[0]
176
+
177
+ conn.close()
178
+
179
+ # If 3+ failures in a week, create an instinct
180
+ if recent_failures >= 3:
181
+ _add_instinct(f"CAUTION: {action_type}", feedback)
182
+
183
+
184
+ @training_sink("learning.add_instinct")
185
+ def _add_instinct(title: str, description: str):
186
+ """Add a new instinct to instincts.md."""
187
+ if not INSTINCTS_FILE.exists():
188
+ INSTINCTS_FILE.write_text("# Assistant Instincts\n\nLearned behaviors and reflexes.\n\n")
189
+
190
+ content = INSTINCTS_FILE.read_text()
191
+
192
+ # Check if already exists
193
+ if title.lower() in content.lower():
194
+ return
195
+
196
+ # Add new instinct
197
+ new_instinct = f"\n## {title}\n\n{description}\n\n*Learned: {datetime.now().strftime('%Y-%m-%d')}*\n"
198
+ INSTINCTS_FILE.write_text(content + new_instinct)
199
+
200
+
201
+ def get_stats(days: int = 7) -> Dict:
202
+ """Get learning statistics."""
203
+ conn = sqlite3.connect(str(LEARNING_DB))
204
+
205
+ cutoff = (datetime.now() - timedelta(days=days)).isoformat()
206
+
207
+ total = conn.execute(
208
+ "SELECT COUNT(*) FROM interactions WHERE timestamp > ?", (cutoff,)
209
+ ).fetchone()[0]
210
+
211
+ successes = conn.execute(
212
+ "SELECT COUNT(*) FROM interactions WHERE timestamp > ? AND success = 1", (cutoff,)
213
+ ).fetchone()[0]
214
+
215
+ by_type = conn.execute("""
216
+ SELECT action_type, COUNT(*), SUM(success)
217
+ FROM interactions WHERE timestamp > ?
218
+ GROUP BY action_type
219
+ ORDER BY COUNT(*) DESC LIMIT 10
220
+ """, (cutoff,)).fetchall()
221
+
222
+ recent_failures = conn.execute("""
223
+ SELECT action_type, action_detail, feedback
224
+ FROM interactions WHERE timestamp > ? AND success = 0
225
+ ORDER BY timestamp DESC LIMIT 5
226
+ """, (cutoff,)).fetchall()
227
+
228
+ conn.close()
229
+
230
+ return {
231
+ "period_days": days,
232
+ "total_interactions": total,
233
+ "successes": successes,
234
+ "success_rate": round(successes / total * 100, 1) if total > 0 else 0,
235
+ "by_type": [
236
+ {"type": t, "count": c, "successes": s, "rate": round(s/c*100, 1) if c > 0 else 0}
237
+ for t, c, s in by_type
238
+ ],
239
+ "recent_failures": [
240
+ {"type": t, "detail": d[:50], "feedback": f}
241
+ for t, d, f in recent_failures
242
+ ]
243
+ }
244
+
245
+
246
+ def get_patterns() -> List[Dict]:
247
+ """Get learned patterns."""
248
+ conn = sqlite3.connect(str(LEARNING_DB))
249
+
250
+ patterns = conn.execute("""
251
+ SELECT pattern_type, description, frequency, success_rate, last_seen, action
252
+ FROM patterns ORDER BY frequency DESC LIMIT 20
253
+ """).fetchall()
254
+
255
+ conn.close()
256
+
257
+ return [
258
+ {
259
+ "type": p[0],
260
+ "description": p[1],
261
+ "frequency": p[2],
262
+ "success_rate": round(p[3] * 100, 1),
263
+ "last_seen": p[4],
264
+ "action": p[5]
265
+ }
266
+ for p in patterns
267
+ ]
268
+
269
+
270
+ @training_sink("learning.reflect")
271
+ def reflect(period: str = "day") -> Dict:
272
+ """Generate a reflection on recent performance."""
273
+ if period == "day":
274
+ days = 1
275
+ elif period == "week":
276
+ days = 7
277
+ elif period == "month":
278
+ days = 30
279
+ else:
280
+ days = 7
281
+
282
+ stats = get_stats(days)
283
+
284
+ # Generate insights
285
+ insights = []
286
+
287
+ if stats["success_rate"] < 80:
288
+ insights.append(f"Success rate ({stats['success_rate']}%) is below target (80%)")
289
+
290
+ # Find problematic areas
291
+ for item in stats["by_type"]:
292
+ if item["rate"] < 70 and item["count"] >= 3:
293
+ insights.append(f"{item['type']}: Only {item['rate']}% success rate over {item['count']} attempts")
294
+
295
+ # Suggest improvements
296
+ improvements = []
297
+ for failure in stats["recent_failures"]:
298
+ if failure["feedback"]:
299
+ improvements.append(f"For {failure['type']}: {failure['feedback']}")
300
+
301
+ reflection = {
302
+ "period": period,
303
+ "stats": stats,
304
+ "insights": insights,
305
+ "improvements": improvements,
306
+ "generated_at": datetime.now().isoformat()
307
+ }
308
+
309
+ # Save reflection
310
+ conn = sqlite3.connect(str(LEARNING_DB))
311
+ conn.execute("""
312
+ INSERT INTO reflections (timestamp, period, summary, insights, improvements)
313
+ VALUES (?, ?, ?, ?, ?)
314
+ """, (
315
+ datetime.now().isoformat(),
316
+ period,
317
+ json.dumps(stats),
318
+ json.dumps(insights),
319
+ json.dumps(improvements)
320
+ ))
321
+ conn.commit()
322
+ conn.close()
323
+
324
+ return reflection
325
+
326
+
327
+ def get_advice(action_type: str) -> str:
328
+ """Get advice based on past learnings for an action type."""
329
+ conn = sqlite3.connect(str(LEARNING_DB))
330
+
331
+ # Get pattern stats
332
+ pattern = conn.execute(
333
+ "SELECT success_rate, frequency FROM patterns WHERE pattern_type = ?",
334
+ (action_type,)
335
+ ).fetchone()
336
+
337
+ # Get recent learnings
338
+ learnings = conn.execute("""
339
+ SELECT learned FROM interactions
340
+ WHERE action_type = ? AND learned IS NOT NULL
341
+ ORDER BY timestamp DESC LIMIT 3
342
+ """, (action_type,)).fetchall()
343
+
344
+ conn.close()
345
+
346
+ advice_parts = []
347
+
348
+ if pattern:
349
+ rate, freq = pattern
350
+ if rate < 0.7:
351
+ advice_parts.append(f"Caution: {action_type} has {rate*100:.0f}% success rate over {freq} attempts.")
352
+ elif rate > 0.9:
353
+ advice_parts.append(f"Good track record: {action_type} has {rate*100:.0f}% success rate.")
354
+
355
+ if learnings:
356
+ advice_parts.append("Past learnings:")
357
+ for (learning,) in learnings:
358
+ advice_parts.append(f" - {learning}")
359
+
360
+ return "\n".join(advice_parts) if advice_parts else f"No prior data for {action_type}"
361
+
362
+
363
+ def log_success(action_type: str, detail: str, context: Dict = None) -> str:
364
+ """Shorthand for logging a success."""
365
+ return log_interaction(action_type, detail, "completed successfully", True, context=context)
366
+
367
+
368
+ def log_failure(action_type: str, detail: str, error: str, feedback: str = None) -> str:
369
+ """Shorthand for logging a failure."""
370
+ return log_interaction(action_type, detail, error, False, feedback=feedback)
371
+
372
+
373
+ @training_sink("learning.log_feedback")
374
+ def log_feedback(feedback: str, action_type: str = None) -> str:
375
+ """Log user feedback on recent actions."""
376
+ conn = sqlite3.connect(str(LEARNING_DB))
377
+
378
+ # Update most recent interaction
379
+ if action_type:
380
+ conn.execute("""
381
+ UPDATE interactions SET feedback = ?
382
+ WHERE id = (SELECT id FROM interactions WHERE action_type = ?
383
+ ORDER BY timestamp DESC LIMIT 1)
384
+ """, (feedback, action_type))
385
+ else:
386
+ conn.execute("""
387
+ UPDATE interactions SET feedback = ?
388
+ WHERE id = (SELECT id FROM interactions ORDER BY timestamp DESC LIMIT 1)
389
+ """, (feedback,))
390
+
391
+ conn.commit()
392
+ conn.close()
393
+
394
+ return f"Feedback recorded: {feedback[:50]}..."
395
+
396
+
397
+ # CLI functions for TUI integration
398
+ def learning_log(action: str, detail: str, success: bool, feedback: str = None) -> str:
399
+ """Log an action for learning."""
400
+ outcome = "success" if success else "failure"
401
+ return log_interaction(action, detail, outcome, success, feedback=feedback)
402
+
403
+
404
+ def learning_stats(days: int = 7) -> Dict:
405
+ """Get learning statistics."""
406
+ return get_stats(days)
407
+
408
+
409
+ def learning_patterns() -> List[Dict]:
410
+ """Get learned patterns."""
411
+ return get_patterns()
412
+
413
+
414
+ def learning_reflect(period: str = "week") -> Dict:
415
+ """Generate a reflection."""
416
+ return reflect(period)
417
+
418
+
419
+ def learning_advice(action_type: str) -> str:
420
+ """Get advice for an action type."""
421
+ return get_advice(action_type)
422
+
423
+
424
+ def learning_feedback(feedback: str) -> str:
425
+ """Record feedback on recent action."""
426
+ return log_feedback(feedback)
427
+
428
+
429
+ if __name__ == "__main__":
430
+ import sys
431
+
432
+ if len(sys.argv) < 2:
433
+ print("Usage: learning.py <log|stats|patterns|reflect|advice> [args]")
434
+ sys.exit(1)
435
+
436
+ cmd = sys.argv[1]
437
+
438
+ if cmd == "log":
439
+ action = sys.argv[2] if len(sys.argv) > 2 else "test"
440
+ detail = sys.argv[3] if len(sys.argv) > 3 else "test action"
441
+ success = sys.argv[4].lower() == "true" if len(sys.argv) > 4 else True
442
+ print(learning_log(action, detail, success))
443
+
444
+ elif cmd == "stats":
445
+ days = int(sys.argv[2]) if len(sys.argv) > 2 else 7
446
+ print(json.dumps(learning_stats(days), indent=2))
447
+
448
+ elif cmd == "patterns":
449
+ print(json.dumps(learning_patterns(), indent=2))
450
+
451
+ elif cmd == "reflect":
452
+ period = sys.argv[2] if len(sys.argv) > 2 else "week"
453
+ print(json.dumps(learning_reflect(period), indent=2))
454
+
455
+ elif cmd == "advice":
456
+ action = sys.argv[2] if len(sys.argv) > 2 else ""
457
+ print(learning_advice(action))
458
+
459
+ else:
460
+ print(f"Unknown command: {cmd}")