attune-ai 2.0.0__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 (457) hide show
  1. attune/__init__.py +358 -0
  2. attune/adaptive/__init__.py +13 -0
  3. attune/adaptive/task_complexity.py +127 -0
  4. attune/agent_monitoring.py +414 -0
  5. attune/cache/__init__.py +117 -0
  6. attune/cache/base.py +166 -0
  7. attune/cache/dependency_manager.py +256 -0
  8. attune/cache/hash_only.py +251 -0
  9. attune/cache/hybrid.py +457 -0
  10. attune/cache/storage.py +285 -0
  11. attune/cache_monitor.py +356 -0
  12. attune/cache_stats.py +298 -0
  13. attune/cli/__init__.py +152 -0
  14. attune/cli/__main__.py +12 -0
  15. attune/cli/commands/__init__.py +1 -0
  16. attune/cli/commands/batch.py +264 -0
  17. attune/cli/commands/cache.py +248 -0
  18. attune/cli/commands/help.py +331 -0
  19. attune/cli/commands/info.py +140 -0
  20. attune/cli/commands/inspect.py +436 -0
  21. attune/cli/commands/inspection.py +57 -0
  22. attune/cli/commands/memory.py +48 -0
  23. attune/cli/commands/metrics.py +92 -0
  24. attune/cli/commands/orchestrate.py +184 -0
  25. attune/cli/commands/patterns.py +207 -0
  26. attune/cli/commands/profiling.py +202 -0
  27. attune/cli/commands/provider.py +98 -0
  28. attune/cli/commands/routing.py +285 -0
  29. attune/cli/commands/setup.py +96 -0
  30. attune/cli/commands/status.py +235 -0
  31. attune/cli/commands/sync.py +166 -0
  32. attune/cli/commands/tier.py +121 -0
  33. attune/cli/commands/utilities.py +114 -0
  34. attune/cli/commands/workflow.py +579 -0
  35. attune/cli/core.py +32 -0
  36. attune/cli/parsers/__init__.py +68 -0
  37. attune/cli/parsers/batch.py +118 -0
  38. attune/cli/parsers/cache.py +65 -0
  39. attune/cli/parsers/help.py +41 -0
  40. attune/cli/parsers/info.py +26 -0
  41. attune/cli/parsers/inspect.py +66 -0
  42. attune/cli/parsers/metrics.py +42 -0
  43. attune/cli/parsers/orchestrate.py +61 -0
  44. attune/cli/parsers/patterns.py +54 -0
  45. attune/cli/parsers/provider.py +40 -0
  46. attune/cli/parsers/routing.py +110 -0
  47. attune/cli/parsers/setup.py +42 -0
  48. attune/cli/parsers/status.py +47 -0
  49. attune/cli/parsers/sync.py +31 -0
  50. attune/cli/parsers/tier.py +33 -0
  51. attune/cli/parsers/workflow.py +77 -0
  52. attune/cli/utils/__init__.py +1 -0
  53. attune/cli/utils/data.py +242 -0
  54. attune/cli/utils/helpers.py +68 -0
  55. attune/cli_legacy.py +3957 -0
  56. attune/cli_minimal.py +1159 -0
  57. attune/cli_router.py +437 -0
  58. attune/cli_unified.py +814 -0
  59. attune/config/__init__.py +66 -0
  60. attune/config/xml_config.py +286 -0
  61. attune/config.py +545 -0
  62. attune/coordination.py +870 -0
  63. attune/core.py +1511 -0
  64. attune/core_modules/__init__.py +15 -0
  65. attune/cost_tracker.py +626 -0
  66. attune/dashboard/__init__.py +41 -0
  67. attune/dashboard/app.py +512 -0
  68. attune/dashboard/simple_server.py +435 -0
  69. attune/dashboard/standalone_server.py +547 -0
  70. attune/discovery.py +306 -0
  71. attune/emergence.py +306 -0
  72. attune/exceptions.py +123 -0
  73. attune/feedback_loops.py +373 -0
  74. attune/hot_reload/README.md +473 -0
  75. attune/hot_reload/__init__.py +62 -0
  76. attune/hot_reload/config.py +83 -0
  77. attune/hot_reload/integration.py +229 -0
  78. attune/hot_reload/reloader.py +298 -0
  79. attune/hot_reload/watcher.py +183 -0
  80. attune/hot_reload/websocket.py +177 -0
  81. attune/levels.py +577 -0
  82. attune/leverage_points.py +441 -0
  83. attune/logging_config.py +261 -0
  84. attune/mcp/__init__.py +10 -0
  85. attune/mcp/server.py +506 -0
  86. attune/memory/__init__.py +237 -0
  87. attune/memory/claude_memory.py +469 -0
  88. attune/memory/config.py +224 -0
  89. attune/memory/control_panel.py +1290 -0
  90. attune/memory/control_panel_support.py +145 -0
  91. attune/memory/cross_session.py +845 -0
  92. attune/memory/edges.py +179 -0
  93. attune/memory/encryption.py +159 -0
  94. attune/memory/file_session.py +770 -0
  95. attune/memory/graph.py +570 -0
  96. attune/memory/long_term.py +913 -0
  97. attune/memory/long_term_types.py +99 -0
  98. attune/memory/mixins/__init__.py +25 -0
  99. attune/memory/mixins/backend_init_mixin.py +249 -0
  100. attune/memory/mixins/capabilities_mixin.py +208 -0
  101. attune/memory/mixins/handoff_mixin.py +208 -0
  102. attune/memory/mixins/lifecycle_mixin.py +49 -0
  103. attune/memory/mixins/long_term_mixin.py +352 -0
  104. attune/memory/mixins/promotion_mixin.py +109 -0
  105. attune/memory/mixins/short_term_mixin.py +182 -0
  106. attune/memory/nodes.py +179 -0
  107. attune/memory/redis_bootstrap.py +540 -0
  108. attune/memory/security/__init__.py +31 -0
  109. attune/memory/security/audit_logger.py +932 -0
  110. attune/memory/security/pii_scrubber.py +640 -0
  111. attune/memory/security/secrets_detector.py +678 -0
  112. attune/memory/short_term.py +2192 -0
  113. attune/memory/simple_storage.py +302 -0
  114. attune/memory/storage/__init__.py +15 -0
  115. attune/memory/storage_backend.py +167 -0
  116. attune/memory/summary_index.py +583 -0
  117. attune/memory/types.py +446 -0
  118. attune/memory/unified.py +182 -0
  119. attune/meta_workflows/__init__.py +74 -0
  120. attune/meta_workflows/agent_creator.py +248 -0
  121. attune/meta_workflows/builtin_templates.py +567 -0
  122. attune/meta_workflows/cli_commands/__init__.py +56 -0
  123. attune/meta_workflows/cli_commands/agent_commands.py +321 -0
  124. attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
  125. attune/meta_workflows/cli_commands/config_commands.py +232 -0
  126. attune/meta_workflows/cli_commands/memory_commands.py +182 -0
  127. attune/meta_workflows/cli_commands/template_commands.py +354 -0
  128. attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
  129. attune/meta_workflows/cli_meta_workflows.py +59 -0
  130. attune/meta_workflows/form_engine.py +292 -0
  131. attune/meta_workflows/intent_detector.py +409 -0
  132. attune/meta_workflows/models.py +569 -0
  133. attune/meta_workflows/pattern_learner.py +738 -0
  134. attune/meta_workflows/plan_generator.py +384 -0
  135. attune/meta_workflows/session_context.py +397 -0
  136. attune/meta_workflows/template_registry.py +229 -0
  137. attune/meta_workflows/workflow.py +984 -0
  138. attune/metrics/__init__.py +12 -0
  139. attune/metrics/collector.py +31 -0
  140. attune/metrics/prompt_metrics.py +194 -0
  141. attune/models/__init__.py +172 -0
  142. attune/models/__main__.py +13 -0
  143. attune/models/adaptive_routing.py +437 -0
  144. attune/models/auth_cli.py +444 -0
  145. attune/models/auth_strategy.py +450 -0
  146. attune/models/cli.py +655 -0
  147. attune/models/empathy_executor.py +354 -0
  148. attune/models/executor.py +257 -0
  149. attune/models/fallback.py +762 -0
  150. attune/models/provider_config.py +282 -0
  151. attune/models/registry.py +472 -0
  152. attune/models/tasks.py +359 -0
  153. attune/models/telemetry/__init__.py +71 -0
  154. attune/models/telemetry/analytics.py +594 -0
  155. attune/models/telemetry/backend.py +196 -0
  156. attune/models/telemetry/data_models.py +431 -0
  157. attune/models/telemetry/storage.py +489 -0
  158. attune/models/token_estimator.py +420 -0
  159. attune/models/validation.py +280 -0
  160. attune/monitoring/__init__.py +52 -0
  161. attune/monitoring/alerts.py +946 -0
  162. attune/monitoring/alerts_cli.py +448 -0
  163. attune/monitoring/multi_backend.py +271 -0
  164. attune/monitoring/otel_backend.py +362 -0
  165. attune/optimization/__init__.py +19 -0
  166. attune/optimization/context_optimizer.py +272 -0
  167. attune/orchestration/__init__.py +67 -0
  168. attune/orchestration/agent_templates.py +707 -0
  169. attune/orchestration/config_store.py +499 -0
  170. attune/orchestration/execution_strategies.py +2111 -0
  171. attune/orchestration/meta_orchestrator.py +1168 -0
  172. attune/orchestration/pattern_learner.py +696 -0
  173. attune/orchestration/real_tools.py +931 -0
  174. attune/pattern_cache.py +187 -0
  175. attune/pattern_library.py +542 -0
  176. attune/patterns/debugging/all_patterns.json +81 -0
  177. attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
  178. attune/patterns/refactoring_memory.json +89 -0
  179. attune/persistence.py +564 -0
  180. attune/platform_utils.py +265 -0
  181. attune/plugins/__init__.py +28 -0
  182. attune/plugins/base.py +361 -0
  183. attune/plugins/registry.py +268 -0
  184. attune/project_index/__init__.py +32 -0
  185. attune/project_index/cli.py +335 -0
  186. attune/project_index/index.py +667 -0
  187. attune/project_index/models.py +504 -0
  188. attune/project_index/reports.py +474 -0
  189. attune/project_index/scanner.py +777 -0
  190. attune/project_index/scanner_parallel.py +291 -0
  191. attune/prompts/__init__.py +61 -0
  192. attune/prompts/config.py +77 -0
  193. attune/prompts/context.py +177 -0
  194. attune/prompts/parser.py +285 -0
  195. attune/prompts/registry.py +313 -0
  196. attune/prompts/templates.py +208 -0
  197. attune/redis_config.py +302 -0
  198. attune/redis_memory.py +799 -0
  199. attune/resilience/__init__.py +56 -0
  200. attune/resilience/circuit_breaker.py +256 -0
  201. attune/resilience/fallback.py +179 -0
  202. attune/resilience/health.py +300 -0
  203. attune/resilience/retry.py +209 -0
  204. attune/resilience/timeout.py +135 -0
  205. attune/routing/__init__.py +43 -0
  206. attune/routing/chain_executor.py +433 -0
  207. attune/routing/classifier.py +217 -0
  208. attune/routing/smart_router.py +234 -0
  209. attune/routing/workflow_registry.py +343 -0
  210. attune/scaffolding/README.md +589 -0
  211. attune/scaffolding/__init__.py +35 -0
  212. attune/scaffolding/__main__.py +14 -0
  213. attune/scaffolding/cli.py +240 -0
  214. attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
  215. attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
  216. attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
  217. attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
  218. attune/socratic/__init__.py +256 -0
  219. attune/socratic/ab_testing.py +958 -0
  220. attune/socratic/blueprint.py +533 -0
  221. attune/socratic/cli.py +703 -0
  222. attune/socratic/collaboration.py +1114 -0
  223. attune/socratic/domain_templates.py +924 -0
  224. attune/socratic/embeddings.py +738 -0
  225. attune/socratic/engine.py +794 -0
  226. attune/socratic/explainer.py +682 -0
  227. attune/socratic/feedback.py +772 -0
  228. attune/socratic/forms.py +629 -0
  229. attune/socratic/generator.py +732 -0
  230. attune/socratic/llm_analyzer.py +637 -0
  231. attune/socratic/mcp_server.py +702 -0
  232. attune/socratic/session.py +312 -0
  233. attune/socratic/storage.py +667 -0
  234. attune/socratic/success.py +730 -0
  235. attune/socratic/visual_editor.py +860 -0
  236. attune/socratic/web_ui.py +958 -0
  237. attune/telemetry/__init__.py +39 -0
  238. attune/telemetry/agent_coordination.py +475 -0
  239. attune/telemetry/agent_tracking.py +367 -0
  240. attune/telemetry/approval_gates.py +545 -0
  241. attune/telemetry/cli.py +1231 -0
  242. attune/telemetry/commands/__init__.py +14 -0
  243. attune/telemetry/commands/dashboard_commands.py +696 -0
  244. attune/telemetry/event_streaming.py +409 -0
  245. attune/telemetry/feedback_loop.py +567 -0
  246. attune/telemetry/usage_tracker.py +591 -0
  247. attune/templates.py +754 -0
  248. attune/test_generator/__init__.py +38 -0
  249. attune/test_generator/__main__.py +14 -0
  250. attune/test_generator/cli.py +234 -0
  251. attune/test_generator/generator.py +355 -0
  252. attune/test_generator/risk_analyzer.py +216 -0
  253. attune/test_generator/templates/unit_test.py.jinja2 +272 -0
  254. attune/tier_recommender.py +384 -0
  255. attune/tools.py +183 -0
  256. attune/trust/__init__.py +28 -0
  257. attune/trust/circuit_breaker.py +579 -0
  258. attune/trust_building.py +527 -0
  259. attune/validation/__init__.py +19 -0
  260. attune/validation/xml_validator.py +281 -0
  261. attune/vscode_bridge.py +173 -0
  262. attune/workflow_commands.py +780 -0
  263. attune/workflow_patterns/__init__.py +33 -0
  264. attune/workflow_patterns/behavior.py +249 -0
  265. attune/workflow_patterns/core.py +76 -0
  266. attune/workflow_patterns/output.py +99 -0
  267. attune/workflow_patterns/registry.py +255 -0
  268. attune/workflow_patterns/structural.py +288 -0
  269. attune/workflows/__init__.py +539 -0
  270. attune/workflows/autonomous_test_gen.py +1268 -0
  271. attune/workflows/base.py +2667 -0
  272. attune/workflows/batch_processing.py +342 -0
  273. attune/workflows/bug_predict.py +1084 -0
  274. attune/workflows/builder.py +273 -0
  275. attune/workflows/caching.py +253 -0
  276. attune/workflows/code_review.py +1048 -0
  277. attune/workflows/code_review_adapters.py +312 -0
  278. attune/workflows/code_review_pipeline.py +722 -0
  279. attune/workflows/config.py +645 -0
  280. attune/workflows/dependency_check.py +644 -0
  281. attune/workflows/document_gen/__init__.py +25 -0
  282. attune/workflows/document_gen/config.py +30 -0
  283. attune/workflows/document_gen/report_formatter.py +162 -0
  284. attune/workflows/document_gen/workflow.py +1426 -0
  285. attune/workflows/document_manager.py +216 -0
  286. attune/workflows/document_manager_README.md +134 -0
  287. attune/workflows/documentation_orchestrator.py +1205 -0
  288. attune/workflows/history.py +510 -0
  289. attune/workflows/keyboard_shortcuts/__init__.py +39 -0
  290. attune/workflows/keyboard_shortcuts/generators.py +391 -0
  291. attune/workflows/keyboard_shortcuts/parsers.py +416 -0
  292. attune/workflows/keyboard_shortcuts/prompts.py +295 -0
  293. attune/workflows/keyboard_shortcuts/schema.py +193 -0
  294. attune/workflows/keyboard_shortcuts/workflow.py +509 -0
  295. attune/workflows/llm_base.py +363 -0
  296. attune/workflows/manage_docs.py +87 -0
  297. attune/workflows/manage_docs_README.md +134 -0
  298. attune/workflows/manage_documentation.py +821 -0
  299. attune/workflows/new_sample_workflow1.py +149 -0
  300. attune/workflows/new_sample_workflow1_README.md +150 -0
  301. attune/workflows/orchestrated_health_check.py +849 -0
  302. attune/workflows/orchestrated_release_prep.py +600 -0
  303. attune/workflows/output.py +413 -0
  304. attune/workflows/perf_audit.py +863 -0
  305. attune/workflows/pr_review.py +762 -0
  306. attune/workflows/progress.py +785 -0
  307. attune/workflows/progress_server.py +322 -0
  308. attune/workflows/progressive/README 2.md +454 -0
  309. attune/workflows/progressive/README.md +454 -0
  310. attune/workflows/progressive/__init__.py +82 -0
  311. attune/workflows/progressive/cli.py +219 -0
  312. attune/workflows/progressive/core.py +488 -0
  313. attune/workflows/progressive/orchestrator.py +723 -0
  314. attune/workflows/progressive/reports.py +520 -0
  315. attune/workflows/progressive/telemetry.py +274 -0
  316. attune/workflows/progressive/test_gen.py +495 -0
  317. attune/workflows/progressive/workflow.py +589 -0
  318. attune/workflows/refactor_plan.py +694 -0
  319. attune/workflows/release_prep.py +895 -0
  320. attune/workflows/release_prep_crew.py +969 -0
  321. attune/workflows/research_synthesis.py +404 -0
  322. attune/workflows/routing.py +168 -0
  323. attune/workflows/secure_release.py +593 -0
  324. attune/workflows/security_adapters.py +297 -0
  325. attune/workflows/security_audit.py +1329 -0
  326. attune/workflows/security_audit_phase3.py +355 -0
  327. attune/workflows/seo_optimization.py +633 -0
  328. attune/workflows/step_config.py +234 -0
  329. attune/workflows/telemetry_mixin.py +269 -0
  330. attune/workflows/test5.py +125 -0
  331. attune/workflows/test5_README.md +158 -0
  332. attune/workflows/test_coverage_boost_crew.py +849 -0
  333. attune/workflows/test_gen/__init__.py +52 -0
  334. attune/workflows/test_gen/ast_analyzer.py +249 -0
  335. attune/workflows/test_gen/config.py +88 -0
  336. attune/workflows/test_gen/data_models.py +38 -0
  337. attune/workflows/test_gen/report_formatter.py +289 -0
  338. attune/workflows/test_gen/test_templates.py +381 -0
  339. attune/workflows/test_gen/workflow.py +655 -0
  340. attune/workflows/test_gen.py +54 -0
  341. attune/workflows/test_gen_behavioral.py +477 -0
  342. attune/workflows/test_gen_parallel.py +341 -0
  343. attune/workflows/test_lifecycle.py +526 -0
  344. attune/workflows/test_maintenance.py +627 -0
  345. attune/workflows/test_maintenance_cli.py +590 -0
  346. attune/workflows/test_maintenance_crew.py +840 -0
  347. attune/workflows/test_runner.py +622 -0
  348. attune/workflows/tier_tracking.py +531 -0
  349. attune/workflows/xml_enhanced_crew.py +285 -0
  350. attune_ai-2.0.0.dist-info/METADATA +1026 -0
  351. attune_ai-2.0.0.dist-info/RECORD +457 -0
  352. attune_ai-2.0.0.dist-info/WHEEL +5 -0
  353. attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
  354. attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
  355. attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
  356. attune_ai-2.0.0.dist-info/top_level.txt +5 -0
  357. attune_healthcare/__init__.py +13 -0
  358. attune_healthcare/monitors/__init__.py +9 -0
  359. attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
  360. attune_healthcare/monitors/monitoring/__init__.py +44 -0
  361. attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
  362. attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
  363. attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
  364. attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
  365. attune_llm/README.md +553 -0
  366. attune_llm/__init__.py +28 -0
  367. attune_llm/agent_factory/__init__.py +53 -0
  368. attune_llm/agent_factory/adapters/__init__.py +85 -0
  369. attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
  370. attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
  371. attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
  372. attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
  373. attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
  374. attune_llm/agent_factory/adapters/native.py +228 -0
  375. attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
  376. attune_llm/agent_factory/base.py +305 -0
  377. attune_llm/agent_factory/crews/__init__.py +67 -0
  378. attune_llm/agent_factory/crews/code_review.py +1113 -0
  379. attune_llm/agent_factory/crews/health_check.py +1262 -0
  380. attune_llm/agent_factory/crews/refactoring.py +1128 -0
  381. attune_llm/agent_factory/crews/security_audit.py +1018 -0
  382. attune_llm/agent_factory/decorators.py +287 -0
  383. attune_llm/agent_factory/factory.py +558 -0
  384. attune_llm/agent_factory/framework.py +193 -0
  385. attune_llm/agent_factory/memory_integration.py +328 -0
  386. attune_llm/agent_factory/resilient.py +320 -0
  387. attune_llm/agents_md/__init__.py +22 -0
  388. attune_llm/agents_md/loader.py +218 -0
  389. attune_llm/agents_md/parser.py +271 -0
  390. attune_llm/agents_md/registry.py +307 -0
  391. attune_llm/claude_memory.py +466 -0
  392. attune_llm/cli/__init__.py +8 -0
  393. attune_llm/cli/sync_claude.py +487 -0
  394. attune_llm/code_health.py +1313 -0
  395. attune_llm/commands/__init__.py +51 -0
  396. attune_llm/commands/context.py +375 -0
  397. attune_llm/commands/loader.py +301 -0
  398. attune_llm/commands/models.py +231 -0
  399. attune_llm/commands/parser.py +371 -0
  400. attune_llm/commands/registry.py +429 -0
  401. attune_llm/config/__init__.py +29 -0
  402. attune_llm/config/unified.py +291 -0
  403. attune_llm/context/__init__.py +22 -0
  404. attune_llm/context/compaction.py +455 -0
  405. attune_llm/context/manager.py +434 -0
  406. attune_llm/contextual_patterns.py +361 -0
  407. attune_llm/core.py +907 -0
  408. attune_llm/git_pattern_extractor.py +435 -0
  409. attune_llm/hooks/__init__.py +24 -0
  410. attune_llm/hooks/config.py +306 -0
  411. attune_llm/hooks/executor.py +289 -0
  412. attune_llm/hooks/registry.py +302 -0
  413. attune_llm/hooks/scripts/__init__.py +39 -0
  414. attune_llm/hooks/scripts/evaluate_session.py +201 -0
  415. attune_llm/hooks/scripts/first_time_init.py +285 -0
  416. attune_llm/hooks/scripts/pre_compact.py +207 -0
  417. attune_llm/hooks/scripts/session_end.py +183 -0
  418. attune_llm/hooks/scripts/session_start.py +163 -0
  419. attune_llm/hooks/scripts/suggest_compact.py +225 -0
  420. attune_llm/learning/__init__.py +30 -0
  421. attune_llm/learning/evaluator.py +438 -0
  422. attune_llm/learning/extractor.py +514 -0
  423. attune_llm/learning/storage.py +560 -0
  424. attune_llm/levels.py +227 -0
  425. attune_llm/pattern_confidence.py +414 -0
  426. attune_llm/pattern_resolver.py +272 -0
  427. attune_llm/pattern_summary.py +350 -0
  428. attune_llm/providers.py +967 -0
  429. attune_llm/routing/__init__.py +32 -0
  430. attune_llm/routing/model_router.py +362 -0
  431. attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
  432. attune_llm/security/PHASE2_COMPLETE.md +384 -0
  433. attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
  434. attune_llm/security/QUICK_REFERENCE.md +316 -0
  435. attune_llm/security/README.md +262 -0
  436. attune_llm/security/__init__.py +62 -0
  437. attune_llm/security/audit_logger.py +929 -0
  438. attune_llm/security/audit_logger_example.py +152 -0
  439. attune_llm/security/pii_scrubber.py +640 -0
  440. attune_llm/security/secrets_detector.py +678 -0
  441. attune_llm/security/secrets_detector_example.py +304 -0
  442. attune_llm/security/secure_memdocs.py +1192 -0
  443. attune_llm/security/secure_memdocs_example.py +278 -0
  444. attune_llm/session_status.py +745 -0
  445. attune_llm/state.py +246 -0
  446. attune_llm/utils/__init__.py +5 -0
  447. attune_llm/utils/tokens.py +349 -0
  448. attune_software/SOFTWARE_PLUGIN_README.md +57 -0
  449. attune_software/__init__.py +13 -0
  450. attune_software/cli/__init__.py +120 -0
  451. attune_software/cli/inspect.py +362 -0
  452. attune_software/cli.py +574 -0
  453. attune_software/plugin.py +188 -0
  454. workflow_scaffolding/__init__.py +11 -0
  455. workflow_scaffolding/__main__.py +12 -0
  456. workflow_scaffolding/cli.py +206 -0
  457. workflow_scaffolding/generator.py +265 -0
@@ -0,0 +1,39 @@
1
+ """Telemetry tracking for Empathy Framework.
2
+
3
+ Privacy-first, local-only usage tracking to measure actual cost savings.
4
+
5
+ Includes:
6
+ - UsageTracker: Track LLM usage and costs
7
+ - HeartbeatCoordinator: Monitor agent liveness via TTL heartbeats
8
+ - CoordinationSignals: Inter-agent communication via TTL signals
9
+ - EventStreamer: Real-time event streaming via Redis Streams
10
+ - ApprovalGate: Human approval gates for workflow control
11
+ - FeedbackLoop: Agent-to-LLM quality feedback for adaptive routing
12
+
13
+ Copyright 2025 Smart-AI-Memory
14
+ Licensed under Fair Source License 0.9
15
+ """
16
+
17
+ from .agent_coordination import CoordinationSignal, CoordinationSignals
18
+ from .agent_tracking import AgentHeartbeat, HeartbeatCoordinator
19
+ from .approval_gates import ApprovalGate, ApprovalRequest, ApprovalResponse
20
+ from .event_streaming import EventStreamer, StreamEvent
21
+ from .feedback_loop import FeedbackEntry, FeedbackLoop, QualityStats, TierRecommendation
22
+ from .usage_tracker import UsageTracker
23
+
24
+ __all__ = [
25
+ "UsageTracker",
26
+ "HeartbeatCoordinator",
27
+ "AgentHeartbeat",
28
+ "CoordinationSignals",
29
+ "CoordinationSignal",
30
+ "EventStreamer",
31
+ "StreamEvent",
32
+ "ApprovalGate",
33
+ "ApprovalRequest",
34
+ "ApprovalResponse",
35
+ "FeedbackLoop",
36
+ "FeedbackEntry",
37
+ "QualityStats",
38
+ "TierRecommendation",
39
+ ]
@@ -0,0 +1,475 @@
1
+ """Agent Coordination via TTL Signals.
2
+
3
+ Pattern 2 from Agent Coordination Architecture - TTL-based inter-agent
4
+ communication for orchestration, synchronization, and coordination.
5
+
6
+ Usage:
7
+ # Agent A signals completion
8
+ coordinator = CoordinationSignals()
9
+ coordinator.signal(
10
+ signal_type="task_complete",
11
+ source_agent="agent-a",
12
+ target_agent="agent-b",
13
+ payload={"result": "success", "data": {...}}
14
+ )
15
+
16
+ # Agent B waits for signal
17
+ signal = coordinator.wait_for_signal(
18
+ signal_type="task_complete",
19
+ source_agent="agent-a",
20
+ timeout=30.0
21
+ )
22
+ if signal:
23
+ process(signal.payload)
24
+
25
+ # Orchestrator broadcasts to all agents
26
+ coordinator.broadcast(
27
+ signal_type="abort",
28
+ source_agent="orchestrator",
29
+ payload={"reason": "user_cancelled"}
30
+ )
31
+
32
+ Copyright 2025 Smart-AI-Memory
33
+ Licensed under Fair Source License 0.9
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import logging
39
+ import time
40
+ from dataclasses import dataclass
41
+ from datetime import datetime
42
+ from typing import TYPE_CHECKING, Any
43
+ from uuid import uuid4
44
+
45
+ if TYPE_CHECKING:
46
+ from attune.memory.types import AgentCredentials
47
+
48
+ logger = logging.getLogger(__name__)
49
+
50
+
51
+ @dataclass
52
+ class CoordinationSignal:
53
+ """Coordination signal between agents.
54
+
55
+ Ephemeral message with TTL, used for agent-to-agent communication.
56
+ """
57
+
58
+ signal_id: str
59
+ signal_type: str # "task_complete", "abort", "ready", "checkpoint", etc.
60
+ source_agent: str
61
+ target_agent: str | None # None for broadcast
62
+ payload: dict[str, Any]
63
+ timestamp: datetime
64
+ ttl_seconds: int = 60
65
+
66
+ def to_dict(self) -> dict[str, Any]:
67
+ """Convert to dictionary for serialization."""
68
+ return {
69
+ "signal_id": self.signal_id,
70
+ "signal_type": self.signal_type,
71
+ "source_agent": self.source_agent,
72
+ "target_agent": self.target_agent,
73
+ "payload": self.payload,
74
+ "timestamp": self.timestamp.isoformat() if isinstance(self.timestamp, datetime) else self.timestamp,
75
+ "ttl_seconds": self.ttl_seconds,
76
+ }
77
+
78
+ @classmethod
79
+ def from_dict(cls, data: dict[str, Any]) -> CoordinationSignal:
80
+ """Create from dictionary."""
81
+ timestamp = data.get("timestamp")
82
+ if isinstance(timestamp, str):
83
+ timestamp = datetime.fromisoformat(timestamp)
84
+ elif not isinstance(timestamp, datetime):
85
+ timestamp = datetime.utcnow()
86
+
87
+ return cls(
88
+ signal_id=data["signal_id"],
89
+ signal_type=data["signal_type"],
90
+ source_agent=data["source_agent"],
91
+ target_agent=data.get("target_agent"),
92
+ payload=data.get("payload", {}),
93
+ timestamp=timestamp,
94
+ ttl_seconds=data.get("ttl_seconds", 60),
95
+ )
96
+
97
+
98
+ class CoordinationSignals:
99
+ """TTL-based inter-agent coordination signals.
100
+
101
+ Agents can:
102
+ - Send signals to specific agents
103
+ - Broadcast signals to all agents
104
+ - Wait for specific signals with timeout
105
+ - Check for pending signals without blocking
106
+
107
+ Signals expire automatically via TTL, preventing stale coordination.
108
+ """
109
+
110
+ DEFAULT_TTL = 60 # Default signal TTL: 60 seconds
111
+ BROADCAST_TARGET = "*" # Special target for broadcast signals
112
+ KEY_PREFIX = "empathy:signal:" # Redis key prefix (consistent with framework)
113
+
114
+ def __init__(self, memory=None, agent_id: str | None = None, enable_streaming: bool = False):
115
+ """Initialize coordination signals.
116
+
117
+ Args:
118
+ memory: Memory instance for storing signals
119
+ agent_id: This agent's ID (for receiving targeted signals)
120
+ enable_streaming: If True, publish signal events to Redis Streams
121
+ for real-time monitoring (Pattern 4).
122
+ """
123
+ self.memory = memory
124
+ self.agent_id = agent_id
125
+ self._enable_streaming = enable_streaming
126
+ self._event_streamer = None
127
+
128
+ if self.memory is None:
129
+ try:
130
+ from attune.telemetry import UsageTracker
131
+
132
+ tracker = UsageTracker.get_instance()
133
+ if hasattr(tracker, "_memory"):
134
+ self.memory = tracker._memory
135
+ except (ImportError, AttributeError):
136
+ pass
137
+
138
+ if self.memory is None:
139
+ logger.warning("No memory backend available for coordination signals")
140
+
141
+ def _get_event_streamer(self):
142
+ """Get or create EventStreamer instance (lazy initialization)."""
143
+ if not self._enable_streaming:
144
+ return None
145
+
146
+ if self._event_streamer is None:
147
+ try:
148
+ from attune.telemetry.event_streaming import EventStreamer
149
+
150
+ self._event_streamer = EventStreamer(memory=self.memory)
151
+ except Exception as e:
152
+ logger.warning(f"Failed to initialize EventStreamer: {e}")
153
+ self._enable_streaming = False
154
+
155
+ return self._event_streamer
156
+
157
+ def signal(
158
+ self,
159
+ signal_type: str,
160
+ source_agent: str | None = None,
161
+ target_agent: str | None = None,
162
+ payload: dict[str, Any] | None = None,
163
+ ttl_seconds: int | None = None,
164
+ credentials: AgentCredentials | None = None,
165
+ ) -> str:
166
+ """Send a coordination signal.
167
+
168
+ Args:
169
+ signal_type: Type of signal (e.g., "task_complete", "abort", "ready")
170
+ source_agent: Source agent ID (defaults to self.agent_id)
171
+ target_agent: Target agent ID (None for broadcast)
172
+ payload: Signal payload data
173
+ ttl_seconds: TTL for this signal (defaults to DEFAULT_TTL)
174
+ credentials: Agent credentials for permission check (optional but recommended)
175
+
176
+ Returns:
177
+ Signal ID
178
+
179
+ Raises:
180
+ PermissionError: If credentials provided but agent lacks CONTRIBUTOR tier
181
+
182
+ Security:
183
+ Coordination signals require CONTRIBUTOR tier or higher. If credentials
184
+ are not provided, a warning is logged but the signal is still sent
185
+ (backward compatibility). For production use, always provide credentials.
186
+ """
187
+ if not self.memory:
188
+ logger.warning("Cannot send signal: no memory backend")
189
+ return ""
190
+
191
+ # Permission check for coordination signals (requires CONTRIBUTOR tier)
192
+ if credentials is not None:
193
+ if not credentials.can_stage():
194
+ raise PermissionError(
195
+ f"Agent {credentials.agent_id} (Tier {credentials.tier.name}) "
196
+ "cannot send coordination signals. Requires CONTRIBUTOR tier or higher."
197
+ )
198
+ else:
199
+ # Log warning if no credentials provided (security best practice)
200
+ logger.warning(
201
+ "Sending coordination signal without credentials - "
202
+ "permission check bypassed. Provide credentials for secure coordination."
203
+ )
204
+
205
+ source = source_agent or self.agent_id or "unknown"
206
+ signal_id = f"signal_{uuid4().hex[:8]}"
207
+ ttl = ttl_seconds or self.DEFAULT_TTL
208
+
209
+ signal = CoordinationSignal(
210
+ signal_id=signal_id,
211
+ signal_type=signal_type,
212
+ source_agent=source,
213
+ target_agent=target_agent,
214
+ payload=payload or {},
215
+ timestamp=datetime.utcnow(),
216
+ ttl_seconds=ttl,
217
+ )
218
+
219
+ # Store signal with TTL (Pattern 2)
220
+ # Key format: empathy:signal:{target}:{type}:{id}
221
+ target_key = target_agent or self.BROADCAST_TARGET
222
+ key = f"{self.KEY_PREFIX}{target_key}:{signal_type}:{signal_id}"
223
+
224
+ try:
225
+ # Use direct Redis access for custom TTL
226
+ if hasattr(self.memory, "_client") and self.memory._client:
227
+ import json
228
+
229
+ self.memory._client.setex(key, ttl, json.dumps(signal.to_dict()))
230
+ else:
231
+ logger.warning("Cannot send signal: no Redis backend available")
232
+ except Exception as e:
233
+ logger.error(f"Failed to send signal {signal_id}: {e}")
234
+
235
+ # Publish to event stream (Pattern 4 - optional)
236
+ streamer = self._get_event_streamer()
237
+ if streamer:
238
+ try:
239
+ streamer.publish_event(
240
+ event_type="coordination_signal",
241
+ data=signal.to_dict(),
242
+ source="attune",
243
+ )
244
+ except Exception as e:
245
+ logger.debug(f"Failed to publish coordination signal event to stream: {e}")
246
+
247
+ return signal_id
248
+
249
+ def broadcast(
250
+ self,
251
+ signal_type: str,
252
+ source_agent: str | None = None,
253
+ payload: dict[str, Any] | None = None,
254
+ ttl_seconds: int | None = None,
255
+ credentials: AgentCredentials | None = None,
256
+ ) -> str:
257
+ """Broadcast signal to all agents.
258
+
259
+ Args:
260
+ signal_type: Type of signal
261
+ source_agent: Source agent ID
262
+ payload: Signal payload
263
+ ttl_seconds: TTL for signal
264
+ credentials: Agent credentials for permission check
265
+
266
+ Returns:
267
+ Signal ID
268
+
269
+ Raises:
270
+ PermissionError: If credentials provided but agent lacks CONTRIBUTOR tier
271
+ """
272
+ return self.signal(
273
+ signal_type=signal_type,
274
+ source_agent=source_agent,
275
+ target_agent=None, # Broadcast
276
+ payload=payload,
277
+ ttl_seconds=ttl_seconds,
278
+ credentials=credentials,
279
+ )
280
+
281
+ def wait_for_signal(
282
+ self,
283
+ signal_type: str,
284
+ source_agent: str | None = None,
285
+ timeout: float = 30.0,
286
+ poll_interval: float = 0.5,
287
+ ) -> CoordinationSignal | None:
288
+ """Wait for a specific signal (blocking with timeout).
289
+
290
+ Args:
291
+ signal_type: Type of signal to wait for
292
+ source_agent: Optional source agent filter
293
+ timeout: Maximum wait time in seconds
294
+ poll_interval: Poll interval in seconds
295
+
296
+ Returns:
297
+ CoordinationSignal if received, None if timeout
298
+ """
299
+ if not self.memory or not self.agent_id:
300
+ return None
301
+
302
+ start_time = time.time()
303
+
304
+ while time.time() - start_time < timeout:
305
+ # Check for signal
306
+ signal = self.check_signal(signal_type=signal_type, source_agent=source_agent, consume=True)
307
+
308
+ if signal:
309
+ return signal
310
+
311
+ # Sleep before next poll
312
+ time.sleep(poll_interval)
313
+
314
+ return None
315
+
316
+ def check_signal(
317
+ self, signal_type: str, source_agent: str | None = None, consume: bool = True
318
+ ) -> CoordinationSignal | None:
319
+ """Check for a signal without blocking.
320
+
321
+ Args:
322
+ signal_type: Type of signal to check
323
+ source_agent: Optional source agent filter
324
+ consume: If True, remove signal after reading
325
+
326
+ Returns:
327
+ CoordinationSignal if available, None otherwise
328
+ """
329
+ if not self.memory or not self.agent_id:
330
+ return None
331
+
332
+ try:
333
+ # Scan for matching signals
334
+ # Check targeted signals: empathy:signal:{agent_id}:{type}:*
335
+ # Check broadcast signals: empathy:signal:*:{type}:*
336
+ patterns = [
337
+ f"{self.KEY_PREFIX}{self.agent_id}:{signal_type}:*",
338
+ f"{self.KEY_PREFIX}{self.BROADCAST_TARGET}:{signal_type}:*"
339
+ ]
340
+
341
+ for pattern in patterns:
342
+ if hasattr(self.memory, "_client"):
343
+ keys = self.memory._client.keys(pattern)
344
+ else:
345
+ continue
346
+
347
+ for key in keys:
348
+ if isinstance(key, bytes):
349
+ key = key.decode("utf-8")
350
+
351
+ # Retrieve signal
352
+ data = self._retrieve_signal(key)
353
+ if not data:
354
+ continue
355
+
356
+ signal = CoordinationSignal.from_dict(data)
357
+
358
+ # Filter by source if specified
359
+ if source_agent and signal.source_agent != source_agent:
360
+ continue
361
+
362
+ # Consume signal if requested
363
+ if consume:
364
+ self._delete_signal(key)
365
+
366
+ return signal
367
+
368
+ return None
369
+ except Exception as e:
370
+ logger.error(f"Failed to check signal: {e}")
371
+ return None
372
+
373
+ def get_pending_signals(self, signal_type: str | None = None) -> list[CoordinationSignal]:
374
+ """Get all pending signals for this agent.
375
+
376
+ Args:
377
+ signal_type: Optional filter by signal type
378
+
379
+ Returns:
380
+ List of pending signals
381
+ """
382
+ if not self.memory or not self.agent_id:
383
+ return []
384
+
385
+ try:
386
+ # Scan for all signals for this agent
387
+ patterns = [
388
+ f"{self.KEY_PREFIX}{self.agent_id}:*",
389
+ f"{self.KEY_PREFIX}{self.BROADCAST_TARGET}:*",
390
+ ]
391
+
392
+ signals = []
393
+ for pattern in patterns:
394
+ if hasattr(self.memory, "_client"):
395
+ keys = self.memory._client.keys(pattern)
396
+ else:
397
+ continue
398
+
399
+ for key in keys:
400
+ if isinstance(key, bytes):
401
+ key = key.decode("utf-8")
402
+
403
+ data = self._retrieve_signal(key)
404
+ if not data:
405
+ continue
406
+
407
+ signal = CoordinationSignal.from_dict(data)
408
+
409
+ # Filter by type if specified
410
+ if signal_type and signal.signal_type != signal_type:
411
+ continue
412
+
413
+ signals.append(signal)
414
+
415
+ return signals
416
+ except Exception as e:
417
+ logger.error(f"Failed to get pending signals: {e}")
418
+ return []
419
+
420
+ def clear_signals(self, signal_type: str | None = None) -> int:
421
+ """Clear all signals for this agent.
422
+
423
+ Args:
424
+ signal_type: Optional filter by signal type
425
+
426
+ Returns:
427
+ Number of signals cleared
428
+ """
429
+ if not self.memory or not self.agent_id:
430
+ return 0
431
+
432
+ signals = self.get_pending_signals(signal_type=signal_type)
433
+ count = 0
434
+
435
+ for signal in signals:
436
+ # Reconstruct key
437
+ target_key = signal.target_agent or self.BROADCAST_TARGET
438
+ key = f"{self.KEY_PREFIX}{target_key}:{signal.signal_type}:{signal.signal_id}"
439
+ if self._delete_signal(key):
440
+ count += 1
441
+
442
+ return count
443
+
444
+ def _retrieve_signal(self, key: str) -> dict[str, Any] | None:
445
+ """Retrieve signal data from memory."""
446
+ if not self.memory:
447
+ return None
448
+
449
+ try:
450
+ # Use direct Redis access (signal keys are stored without prefix)
451
+ if hasattr(self.memory, "_client"):
452
+ import json
453
+
454
+ data = self.memory._client.get(key)
455
+ if data:
456
+ if isinstance(data, bytes):
457
+ data = data.decode("utf-8")
458
+ return json.loads(data)
459
+ return None
460
+ except Exception as e:
461
+ logger.debug(f"Failed to retrieve signal {key}: {e}")
462
+ return None
463
+
464
+ def _delete_signal(self, key: str) -> bool:
465
+ """Delete signal from memory."""
466
+ if not self.memory:
467
+ return False
468
+
469
+ try:
470
+ if hasattr(self.memory, "_client"):
471
+ return self.memory._client.delete(key) > 0
472
+ return False
473
+ except Exception as e:
474
+ logger.debug(f"Failed to delete signal {key}: {e}")
475
+ return False