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,489 @@
1
+ """Telemetry storage implementation.
2
+
3
+ File-based storage for telemetry records.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import json
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+
13
+ from .backend import _parse_timestamp
14
+ from .data_models import (
15
+ AgentAssignmentRecord,
16
+ CoverageRecord,
17
+ FileTestRecord,
18
+ LLMCallRecord,
19
+ TaskRoutingRecord,
20
+ TestExecutionRecord,
21
+ WorkflowRunRecord,
22
+ )
23
+
24
+
25
+ class TelemetryStore:
26
+ """JSONL file-based telemetry backend (default implementation).
27
+
28
+ Stores records in JSONL format for easy streaming and analysis.
29
+ Implements the TelemetryBackend protocol.
30
+
31
+ Supports both core telemetry and Tier 1 automation monitoring.
32
+ """
33
+
34
+ def __init__(self, storage_dir: str = ".empathy"):
35
+ """Initialize telemetry store.
36
+
37
+ Args:
38
+ storage_dir: Directory for telemetry files
39
+
40
+ """
41
+ self.storage_dir = Path(storage_dir)
42
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
43
+
44
+ # Core telemetry files
45
+ self.calls_file = self.storage_dir / "llm_calls.jsonl"
46
+ self.workflows_file = self.storage_dir / "workflow_runs.jsonl"
47
+
48
+ # Tier 1 automation monitoring files
49
+ self.task_routing_file = self.storage_dir / "task_routing.jsonl"
50
+ self.test_executions_file = self.storage_dir / "test_executions.jsonl"
51
+ self.coverage_history_file = self.storage_dir / "coverage_history.jsonl"
52
+ self.agent_assignments_file = self.storage_dir / "agent_assignments.jsonl"
53
+
54
+ # Per-file test tracking
55
+ self.file_tests_file = self.storage_dir / "file_tests.jsonl"
56
+
57
+ def log_call(self, record: LLMCallRecord) -> None:
58
+ """Log an LLM call record."""
59
+ with open(self.calls_file, "a") as f:
60
+ f.write(json.dumps(record.to_dict()) + "\n")
61
+
62
+ def log_workflow(self, record: WorkflowRunRecord) -> None:
63
+ """Log a workflow run record."""
64
+ with open(self.workflows_file, "a") as f:
65
+ f.write(json.dumps(record.to_dict()) + "\n")
66
+
67
+ def get_calls(
68
+ self,
69
+ since: datetime | None = None,
70
+ workflow_name: str | None = None,
71
+ limit: int = 1000,
72
+ ) -> list[LLMCallRecord]:
73
+ """Get LLM call records.
74
+
75
+ Args:
76
+ since: Only return records after this time
77
+ workflow_name: Filter by workflow name
78
+ limit: Maximum records to return
79
+
80
+ Returns:
81
+ List of LLMCallRecord
82
+
83
+ """
84
+ records: list[LLMCallRecord] = []
85
+ if not self.calls_file.exists():
86
+ return records
87
+
88
+ with open(self.calls_file) as f:
89
+ for line in f:
90
+ if not line.strip():
91
+ continue
92
+ try:
93
+ data = json.loads(line)
94
+ record = LLMCallRecord.from_dict(data)
95
+
96
+ # Apply filters
97
+ if since:
98
+ record_time = _parse_timestamp(record.timestamp)
99
+ if record_time < since:
100
+ continue
101
+
102
+ if workflow_name and record.workflow_name != workflow_name:
103
+ continue
104
+
105
+ records.append(record)
106
+
107
+ if len(records) >= limit:
108
+ break
109
+ except (json.JSONDecodeError, KeyError):
110
+ continue
111
+
112
+ return records
113
+
114
+ def get_workflows(
115
+ self,
116
+ since: datetime | None = None,
117
+ workflow_name: str | None = None,
118
+ limit: int = 100,
119
+ ) -> list[WorkflowRunRecord]:
120
+ """Get workflow run records.
121
+
122
+ Args:
123
+ since: Only return records after this time
124
+ workflow_name: Filter by workflow name
125
+ limit: Maximum records to return
126
+
127
+ Returns:
128
+ List of WorkflowRunRecord
129
+
130
+ """
131
+ records: list[WorkflowRunRecord] = []
132
+ if not self.workflows_file.exists():
133
+ return records
134
+
135
+ with open(self.workflows_file) as f:
136
+ for line in f:
137
+ if not line.strip():
138
+ continue
139
+ try:
140
+ data = json.loads(line)
141
+ record = WorkflowRunRecord.from_dict(data)
142
+
143
+ # Apply filters
144
+ if since:
145
+ record_time = _parse_timestamp(record.started_at)
146
+ if record_time < since:
147
+ continue
148
+
149
+ if workflow_name and record.workflow_name != workflow_name:
150
+ continue
151
+
152
+ records.append(record)
153
+
154
+ if len(records) >= limit:
155
+ break
156
+ except (json.JSONDecodeError, KeyError):
157
+ continue
158
+
159
+ return records
160
+
161
+ # Tier 1 automation monitoring methods
162
+
163
+ def log_task_routing(self, record: TaskRoutingRecord) -> None:
164
+ """Log a task routing decision."""
165
+ with open(self.task_routing_file, "a") as f:
166
+ f.write(json.dumps(record.to_dict()) + "\n")
167
+
168
+ def log_test_execution(self, record: TestExecutionRecord) -> None:
169
+ """Log a test execution."""
170
+ with open(self.test_executions_file, "a") as f:
171
+ f.write(json.dumps(record.to_dict()) + "\n")
172
+
173
+ def log_coverage(self, record: CoverageRecord) -> None:
174
+ """Log coverage metrics."""
175
+ with open(self.coverage_history_file, "a") as f:
176
+ f.write(json.dumps(record.to_dict()) + "\n")
177
+
178
+ def log_agent_assignment(self, record: AgentAssignmentRecord) -> None:
179
+ """Log an agent assignment."""
180
+ with open(self.agent_assignments_file, "a") as f:
181
+ f.write(json.dumps(record.to_dict()) + "\n")
182
+
183
+ def get_task_routings(
184
+ self,
185
+ since: datetime | None = None,
186
+ status: str | None = None,
187
+ limit: int = 1000,
188
+ ) -> list[TaskRoutingRecord]:
189
+ """Get task routing records.
190
+
191
+ Args:
192
+ since: Only return records after this time
193
+ status: Filter by status (pending, running, completed, failed)
194
+ limit: Maximum records to return
195
+
196
+ Returns:
197
+ List of TaskRoutingRecord
198
+
199
+ """
200
+ records: list[TaskRoutingRecord] = []
201
+ if not self.task_routing_file.exists():
202
+ return records
203
+
204
+ with open(self.task_routing_file) as f:
205
+ for line in f:
206
+ if not line.strip():
207
+ continue
208
+ try:
209
+ data = json.loads(line)
210
+ record = TaskRoutingRecord.from_dict(data)
211
+
212
+ # Apply filters
213
+ if since:
214
+ record_time = _parse_timestamp(record.timestamp)
215
+ if record_time < since:
216
+ continue
217
+
218
+ if status and record.status != status:
219
+ continue
220
+
221
+ records.append(record)
222
+
223
+ if len(records) >= limit:
224
+ break
225
+ except (json.JSONDecodeError, KeyError):
226
+ continue
227
+
228
+ return records
229
+
230
+ def get_test_executions(
231
+ self,
232
+ since: datetime | None = None,
233
+ success_only: bool = False,
234
+ limit: int = 100,
235
+ ) -> list[TestExecutionRecord]:
236
+ """Get test execution records.
237
+
238
+ Args:
239
+ since: Only return records after this time
240
+ success_only: Only return successful test runs
241
+ limit: Maximum records to return
242
+
243
+ Returns:
244
+ List of TestExecutionRecord
245
+
246
+ """
247
+ records: list[TestExecutionRecord] = []
248
+ if not self.test_executions_file.exists():
249
+ return records
250
+
251
+ with open(self.test_executions_file) as f:
252
+ for line in f:
253
+ if not line.strip():
254
+ continue
255
+ try:
256
+ data = json.loads(line)
257
+ record = TestExecutionRecord.from_dict(data)
258
+
259
+ # Apply filters
260
+ if since:
261
+ record_time = _parse_timestamp(record.timestamp)
262
+ if record_time < since:
263
+ continue
264
+
265
+ if success_only and not record.success:
266
+ continue
267
+
268
+ records.append(record)
269
+
270
+ if len(records) >= limit:
271
+ break
272
+ except (json.JSONDecodeError, KeyError):
273
+ continue
274
+
275
+ return records
276
+
277
+ def get_coverage_history(
278
+ self,
279
+ since: datetime | None = None,
280
+ limit: int = 100,
281
+ ) -> list[CoverageRecord]:
282
+ """Get coverage history records.
283
+
284
+ Args:
285
+ since: Only return records after this time
286
+ limit: Maximum records to return
287
+
288
+ Returns:
289
+ List of CoverageRecord
290
+
291
+ """
292
+ records: list[CoverageRecord] = []
293
+ if not self.coverage_history_file.exists():
294
+ return records
295
+
296
+ with open(self.coverage_history_file) as f:
297
+ for line in f:
298
+ if not line.strip():
299
+ continue
300
+ try:
301
+ data = json.loads(line)
302
+ record = CoverageRecord.from_dict(data)
303
+
304
+ # Apply filters
305
+ if since:
306
+ record_time = _parse_timestamp(record.timestamp)
307
+ if record_time < since:
308
+ continue
309
+
310
+ records.append(record)
311
+
312
+ if len(records) >= limit:
313
+ break
314
+ except (json.JSONDecodeError, KeyError):
315
+ continue
316
+
317
+ return records
318
+
319
+ def get_agent_assignments(
320
+ self,
321
+ since: datetime | None = None,
322
+ automated_only: bool = True,
323
+ limit: int = 1000,
324
+ ) -> list[AgentAssignmentRecord]:
325
+ """Get agent assignment records.
326
+
327
+ Args:
328
+ since: Only return records after this time
329
+ automated_only: Only return assignments eligible for Tier 1 automation
330
+ limit: Maximum records to return
331
+
332
+ Returns:
333
+ List of AgentAssignmentRecord
334
+
335
+ """
336
+ records: list[AgentAssignmentRecord] = []
337
+ if not self.agent_assignments_file.exists():
338
+ return records
339
+
340
+ with open(self.agent_assignments_file) as f:
341
+ for line in f:
342
+ if not line.strip():
343
+ continue
344
+ try:
345
+ data = json.loads(line)
346
+ record = AgentAssignmentRecord.from_dict(data)
347
+
348
+ # Apply filters
349
+ if since:
350
+ record_time = _parse_timestamp(record.timestamp)
351
+ if record_time < since:
352
+ continue
353
+
354
+ if automated_only and not record.automated_eligible:
355
+ continue
356
+
357
+ records.append(record)
358
+
359
+ if len(records) >= limit:
360
+ break
361
+ except (json.JSONDecodeError, KeyError):
362
+ continue
363
+
364
+ return records
365
+
366
+ # Per-file test tracking methods
367
+
368
+ def log_file_test(self, record: "FileTestRecord") -> None:
369
+ """Log a per-file test execution record.
370
+
371
+ Args:
372
+ record: FileTestRecord to log
373
+ """
374
+ with open(self.file_tests_file, "a") as f:
375
+ f.write(json.dumps(record.to_dict()) + "\n")
376
+
377
+ def get_file_tests(
378
+ self,
379
+ file_path: str | None = None,
380
+ since: datetime | None = None,
381
+ result_filter: str | None = None,
382
+ limit: int = 1000,
383
+ ) -> list["FileTestRecord"]:
384
+ """Get per-file test records with optional filters.
385
+
386
+ Args:
387
+ file_path: Filter by specific file path
388
+ since: Only return records after this time
389
+ result_filter: Filter by result (passed, failed, error, skipped, no_tests)
390
+ limit: Maximum records to return
391
+
392
+ Returns:
393
+ List of FileTestRecord
394
+ """
395
+ records: list[FileTestRecord] = []
396
+ if not self.file_tests_file.exists():
397
+ return records
398
+
399
+ with open(self.file_tests_file) as f:
400
+ for line in f:
401
+ if not line.strip():
402
+ continue
403
+ try:
404
+ data = json.loads(line)
405
+ record = FileTestRecord.from_dict(data)
406
+
407
+ # Apply filters
408
+ if file_path and record.file_path != file_path:
409
+ continue
410
+
411
+ if since:
412
+ record_time = _parse_timestamp(record.timestamp)
413
+ if record_time < since:
414
+ continue
415
+
416
+ if result_filter and record.last_test_result != result_filter:
417
+ continue
418
+
419
+ records.append(record)
420
+
421
+ if len(records) >= limit:
422
+ break
423
+ except (json.JSONDecodeError, KeyError):
424
+ continue
425
+
426
+ return records
427
+
428
+ def get_latest_file_test(self, file_path: str) -> "FileTestRecord | None":
429
+ """Get the most recent test record for a specific file.
430
+
431
+ Args:
432
+ file_path: Path to the source file
433
+
434
+ Returns:
435
+ Most recent FileTestRecord or None if not found
436
+ """
437
+ records = self.get_file_tests(file_path=file_path, limit=10000)
438
+ if not records:
439
+ return None
440
+
441
+ # Return the most recent record (last one since we read in chronological order)
442
+ return records[-1]
443
+
444
+ def get_files_needing_tests(
445
+ self,
446
+ stale_only: bool = False,
447
+ failed_only: bool = False,
448
+ ) -> list["FileTestRecord"]:
449
+ """Get files that need test attention.
450
+
451
+ Args:
452
+ stale_only: Only return files with stale tests
453
+ failed_only: Only return files with failed tests
454
+
455
+ Returns:
456
+ List of FileTestRecord for files needing attention
457
+ """
458
+ all_records = self.get_file_tests(limit=100000)
459
+
460
+ # Get latest record per file
461
+ latest_by_file: dict[str, FileTestRecord] = {}
462
+ for record in all_records:
463
+ existing = latest_by_file.get(record.file_path)
464
+ if existing is None:
465
+ latest_by_file[record.file_path] = record
466
+ else:
467
+ # Keep the more recent one
468
+ if record.timestamp > existing.timestamp:
469
+ latest_by_file[record.file_path] = record
470
+
471
+ # Filter based on criteria
472
+ results = []
473
+ for record in latest_by_file.values():
474
+ if stale_only and not record.is_stale:
475
+ continue
476
+ if failed_only and record.last_test_result not in ("failed", "error"):
477
+ continue
478
+ if not stale_only and not failed_only:
479
+ # Return all files needing attention (stale OR failed OR no_tests)
480
+ if (
481
+ record.last_test_result not in ("failed", "error", "no_tests")
482
+ and not record.is_stale
483
+ ):
484
+ continue
485
+ results.append(record)
486
+
487
+ return results
488
+
489
+