@agentikos/omega-os 0.1.0 → 0.19.5

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 (379) hide show
  1. package/README.md +56 -14
  2. package/bootstrap/lib/__pycache__/claude-code-settings.cpython-313.pyc +0 -0
  3. package/bootstrap/lib/__pycache__/llm-clis.cpython-313.pyc +0 -0
  4. package/bootstrap/lib/__pycache__/manifest-helpers.cpython-313.pyc +0 -0
  5. package/bootstrap/lib/claude-code-settings.py +176 -0
  6. package/bootstrap/lib/common.sh +457 -1
  7. package/bootstrap/lib/llm-clis.py +341 -0
  8. package/bootstrap/lib/manifest-helpers.py +384 -0
  9. package/bootstrap/lib/steps.sh +1000 -26
  10. package/bootstrap/manifest.example.yaml +93 -2
  11. package/bootstrap/templates/aisb/CLAUDE.md +305 -0
  12. package/bootstrap/templates/aisb/architect.md +204 -0
  13. package/bootstrap/templates/aisb/checkers/CLAUDE.md +9 -0
  14. package/bootstrap/templates/aisb/checkers/checker-architect.md +151 -0
  15. package/bootstrap/templates/aisb/checkers/checker-common.md +171 -0
  16. package/bootstrap/templates/aisb/checkers/checker-construct.md +129 -0
  17. package/bootstrap/templates/aisb/checkers/checker-keymaker.md +204 -0
  18. package/bootstrap/templates/aisb/checkers/checker-link.md +205 -0
  19. package/bootstrap/templates/aisb/checkers/checker-merovingian.md +219 -0
  20. package/bootstrap/templates/aisb/checkers/checker-morpheus.md +211 -0
  21. package/bootstrap/templates/aisb/checkers/checker-neo.md +177 -0
  22. package/bootstrap/templates/aisb/checkers/checker-niobe.md +156 -0
  23. package/bootstrap/templates/aisb/checkers/checker-oracle.md +164 -0
  24. package/bootstrap/templates/aisb/checkers/checker-seraph.md +187 -0
  25. package/bootstrap/templates/aisb/checkers/checker-smith.md +195 -0
  26. package/bootstrap/templates/aisb/checkers/checker-zion.md +113 -0
  27. package/bootstrap/templates/aisb/construct.md +135 -0
  28. package/bootstrap/templates/aisb/keymaker.md +227 -0
  29. package/bootstrap/templates/aisb/link.md +170 -0
  30. package/bootstrap/templates/aisb/lmc-protocol.md +57 -0
  31. package/bootstrap/templates/aisb/merovingian.md +159 -0
  32. package/bootstrap/templates/aisb/morpheus.md +243 -0
  33. package/bootstrap/templates/aisb/neo.md +147 -0
  34. package/bootstrap/templates/aisb/niobe.md +197 -0
  35. package/bootstrap/templates/aisb/oracle.md +244 -0
  36. package/bootstrap/templates/aisb/protocols/handoff-templates.md +204 -0
  37. package/bootstrap/templates/aisb/protocols/shared-protocol.md +248 -0
  38. package/bootstrap/templates/aisb/pythia.md +153 -0
  39. package/bootstrap/templates/aisb/seraph.md +315 -0
  40. package/bootstrap/templates/aisb/smith.md +202 -0
  41. package/bootstrap/templates/aisb/zion.md +172 -0
  42. package/bootstrap/templates/autonomous/audit-patrol.yaml +41 -0
  43. package/bootstrap/templates/autonomous/smith-reflect.yaml +43 -0
  44. package/bootstrap/templates/autonomous/ssh-key-rotate.yaml +46 -0
  45. package/bootstrap/templates/autonomous/support-agent.yaml +38 -0
  46. package/docs/AUDITS.md +85 -0
  47. package/docs/COMPLETION-PLAN.md +48 -0
  48. package/docs/GAP-ANALYSIS.md +214 -0
  49. package/docs/INSTALL.md +47 -9
  50. package/docs/MCP-AND-PLUGINS.md +31 -4
  51. package/docs/SIMULATION.md +171 -0
  52. package/docs/simulate.sh +211 -0
  53. package/install.sh +164 -17
  54. package/omega/Agentik_Engine/README.md +27 -10
  55. package/omega/Agentik_Engine/omega_engine/__init__.py +212 -2
  56. package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
  57. package/omega/Agentik_Engine/omega_engine/__pycache__/account.cpython-313.pyc +0 -0
  58. package/omega/Agentik_Engine/omega_engine/__pycache__/agent_messages.cpython-313.pyc +0 -0
  59. package/omega/Agentik_Engine/omega_engine/__pycache__/aisb_chat.cpython-313.pyc +0 -0
  60. package/omega/Agentik_Engine/omega_engine/__pycache__/audit_diff.cpython-313.pyc +0 -0
  61. package/omega/Agentik_Engine/omega_engine/__pycache__/audit_gate.cpython-313.pyc +0 -0
  62. package/omega/Agentik_Engine/omega_engine/__pycache__/auto_update.cpython-313.pyc +0 -0
  63. package/omega/Agentik_Engine/omega_engine/__pycache__/autonomous.cpython-313.pyc +0 -0
  64. package/omega/Agentik_Engine/omega_engine/__pycache__/backup.cpython-313.pyc +0 -0
  65. package/omega/Agentik_Engine/omega_engine/__pycache__/cadence.cpython-313.pyc +0 -0
  66. package/omega/Agentik_Engine/omega_engine/__pycache__/classifier.cpython-313.pyc +0 -0
  67. package/omega/Agentik_Engine/omega_engine/__pycache__/cleanup.cpython-313.pyc +0 -0
  68. package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
  69. package/omega/Agentik_Engine/omega_engine/__pycache__/completions.cpython-313.pyc +0 -0
  70. package/omega/Agentik_Engine/omega_engine/__pycache__/costs.cpython-313.pyc +0 -0
  71. package/omega/Agentik_Engine/omega_engine/__pycache__/done_signal.cpython-313.pyc +0 -0
  72. package/omega/Agentik_Engine/omega_engine/__pycache__/envelope.cpython-313.pyc +0 -0
  73. package/omega/Agentik_Engine/omega_engine/__pycache__/executor.cpython-313.pyc +0 -0
  74. package/omega/Agentik_Engine/omega_engine/__pycache__/handoff.cpython-313.pyc +0 -0
  75. package/omega/Agentik_Engine/omega_engine/__pycache__/hermes.cpython-313.pyc +0 -0
  76. package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_bootstrap.cpython-313.pyc +0 -0
  77. package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_desktop.cpython-313.pyc +0 -0
  78. package/omega/Agentik_Engine/omega_engine/__pycache__/learning.cpython-313.pyc +0 -0
  79. package/omega/Agentik_Engine/omega_engine/__pycache__/managed_agent.cpython-313.pyc +0 -0
  80. package/omega/Agentik_Engine/omega_engine/__pycache__/memory.cpython-313.pyc +0 -0
  81. package/omega/Agentik_Engine/omega_engine/__pycache__/menu.cpython-313.pyc +0 -0
  82. package/omega/Agentik_Engine/omega_engine/__pycache__/mission.cpython-313.pyc +0 -0
  83. package/omega/Agentik_Engine/omega_engine/__pycache__/plan.cpython-313.pyc +0 -0
  84. package/omega/Agentik_Engine/omega_engine/__pycache__/project.cpython-313.pyc +0 -0
  85. package/omega/Agentik_Engine/omega_engine/__pycache__/prompts.cpython-313.pyc +0 -0
  86. package/omega/Agentik_Engine/omega_engine/__pycache__/provider.cpython-313.pyc +0 -0
  87. package/omega/Agentik_Engine/omega_engine/__pycache__/prune.cpython-313.pyc +0 -0
  88. package/omega/Agentik_Engine/omega_engine/__pycache__/pursue.cpython-313.pyc +0 -0
  89. package/omega/Agentik_Engine/omega_engine/__pycache__/reducer.cpython-313.pyc +0 -0
  90. package/omega/Agentik_Engine/omega_engine/__pycache__/router.cpython-313.pyc +0 -0
  91. package/omega/Agentik_Engine/omega_engine/__pycache__/skill_routing.cpython-313.pyc +0 -0
  92. package/omega/Agentik_Engine/omega_engine/__pycache__/smoke.cpython-313.pyc +0 -0
  93. package/omega/Agentik_Engine/omega_engine/__pycache__/store.cpython-313.pyc +0 -0
  94. package/omega/Agentik_Engine/omega_engine/__pycache__/sync.cpython-313.pyc +0 -0
  95. package/omega/Agentik_Engine/omega_engine/__pycache__/telegram_history.cpython-313.pyc +0 -0
  96. package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
  97. package/omega/Agentik_Engine/omega_engine/__pycache__/tools.cpython-313.pyc +0 -0
  98. package/omega/Agentik_Engine/omega_engine/__pycache__/understand_anything.cpython-313.pyc +0 -0
  99. package/omega/Agentik_Engine/omega_engine/__pycache__/updater.cpython-313.pyc +0 -0
  100. package/omega/Agentik_Engine/omega_engine/__pycache__/validate.cpython-313.pyc +0 -0
  101. package/omega/Agentik_Engine/omega_engine/__pycache__/vault.cpython-313.pyc +0 -0
  102. package/omega/Agentik_Engine/omega_engine/__pycache__/webhooks.cpython-313.pyc +0 -0
  103. package/omega/Agentik_Engine/omega_engine/__pycache__/worker.cpython-313.pyc +0 -0
  104. package/omega/Agentik_Engine/omega_engine/account.py +502 -0
  105. package/omega/Agentik_Engine/omega_engine/agent_messages.py +167 -0
  106. package/omega/Agentik_Engine/omega_engine/aisb_chat.py +128 -0
  107. package/omega/Agentik_Engine/omega_engine/audit_diff.py +99 -0
  108. package/omega/Agentik_Engine/omega_engine/audit_gate.py +149 -0
  109. package/omega/Agentik_Engine/omega_engine/audits/__init__.py +60 -0
  110. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/__init__.cpython-313.pyc +0 -0
  111. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/batcher.cpython-313.pyc +0 -0
  112. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/dispatcher.cpython-313.pyc +0 -0
  113. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/generator.cpython-313.pyc +0 -0
  114. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/history.cpython-313.pyc +0 -0
  115. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/pipeline.cpython-313.pyc +0 -0
  116. package/omega/Agentik_Engine/omega_engine/audits/batcher.py +218 -0
  117. package/omega/Agentik_Engine/omega_engine/audits/dispatcher.py +92 -0
  118. package/omega/Agentik_Engine/omega_engine/audits/generator.py +234 -0
  119. package/omega/Agentik_Engine/omega_engine/audits/history.py +168 -0
  120. package/omega/Agentik_Engine/omega_engine/audits/pipeline.py +198 -0
  121. package/omega/Agentik_Engine/omega_engine/auto_update.py +339 -0
  122. package/omega/Agentik_Engine/omega_engine/autonomous.py +538 -0
  123. package/omega/Agentik_Engine/omega_engine/backup.py +215 -0
  124. package/omega/Agentik_Engine/omega_engine/cadence.py +158 -0
  125. package/omega/Agentik_Engine/omega_engine/classifier.py +215 -0
  126. package/omega/Agentik_Engine/omega_engine/cleanup.py +673 -0
  127. package/omega/Agentik_Engine/omega_engine/cli.py +4564 -56
  128. package/omega/Agentik_Engine/omega_engine/completions.py +260 -0
  129. package/omega/Agentik_Engine/omega_engine/costs.py +100 -0
  130. package/omega/Agentik_Engine/omega_engine/daemons/__init__.py +14 -0
  131. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/__init__.cpython-313.pyc +0 -0
  132. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/autonomous.cpython-313.pyc +0 -0
  133. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/engine.cpython-313.pyc +0 -0
  134. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/telegram.cpython-313.pyc +0 -0
  135. package/omega/Agentik_Engine/omega_engine/daemons/autonomous.py +56 -0
  136. package/omega/Agentik_Engine/omega_engine/daemons/engine.py +236 -0
  137. package/omega/Agentik_Engine/omega_engine/daemons/telegram.py +315 -0
  138. package/omega/Agentik_Engine/omega_engine/done_signal.py +154 -0
  139. package/omega/Agentik_Engine/omega_engine/educators/__init__.py +51 -0
  140. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/__init__.cpython-313.pyc +0 -0
  141. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/artifact.cpython-313.pyc +0 -0
  142. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/automation.cpython-313.pyc +0 -0
  143. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/base.cpython-313.pyc +0 -0
  144. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/claudecode.cpython-313.pyc +0 -0
  145. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/connection.cpython-313.pyc +0 -0
  146. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/coworker.cpython-313.pyc +0 -0
  147. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/loop.cpython-313.pyc +0 -0
  148. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/prompt.cpython-313.pyc +0 -0
  149. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/skill.cpython-313.pyc +0 -0
  150. package/omega/Agentik_Engine/omega_engine/educators/artifact.py +65 -0
  151. package/omega/Agentik_Engine/omega_engine/educators/automation.py +76 -0
  152. package/omega/Agentik_Engine/omega_engine/educators/base.py +327 -0
  153. package/omega/Agentik_Engine/omega_engine/educators/claudecode.py +71 -0
  154. package/omega/Agentik_Engine/omega_engine/educators/connection.py +75 -0
  155. package/omega/Agentik_Engine/omega_engine/educators/coworker.py +68 -0
  156. package/omega/Agentik_Engine/omega_engine/educators/loop.py +82 -0
  157. package/omega/Agentik_Engine/omega_engine/educators/prompt.py +68 -0
  158. package/omega/Agentik_Engine/omega_engine/educators/skill.py +69 -0
  159. package/omega/Agentik_Engine/omega_engine/envelope.py +219 -0
  160. package/omega/Agentik_Engine/omega_engine/executor.py +195 -16
  161. package/omega/Agentik_Engine/omega_engine/genesis/__init__.py +134 -0
  162. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/__init__.cpython-313.pyc +0 -0
  163. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/orchestrator.cpython-313.pyc +0 -0
  164. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/phases.cpython-313.pyc +0 -0
  165. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/stack.cpython-313.pyc +0 -0
  166. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/state.cpython-313.pyc +0 -0
  167. package/omega/Agentik_Engine/omega_engine/genesis/orchestrator.py +262 -0
  168. package/omega/Agentik_Engine/omega_engine/genesis/phases.py +950 -0
  169. package/omega/Agentik_Engine/omega_engine/genesis/stack.py +324 -0
  170. package/omega/Agentik_Engine/omega_engine/genesis/state.py +353 -0
  171. package/omega/Agentik_Engine/omega_engine/handoff.py +459 -0
  172. package/omega/Agentik_Engine/omega_engine/hermes.py +426 -0
  173. package/omega/Agentik_Engine/omega_engine/hermes_bootstrap.py +382 -0
  174. package/omega/Agentik_Engine/omega_engine/hermes_desktop.py +469 -0
  175. package/omega/Agentik_Engine/omega_engine/integrations/__init__.py +30 -0
  176. package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/__init__.cpython-313.pyc +0 -0
  177. package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/graphify.cpython-313.pyc +0 -0
  178. package/omega/Agentik_Engine/omega_engine/integrations/graphify.py +234 -0
  179. package/omega/Agentik_Engine/omega_engine/learning.py +268 -0
  180. package/omega/Agentik_Engine/omega_engine/managed_agent.py +467 -0
  181. package/omega/Agentik_Engine/omega_engine/memory.py +271 -0
  182. package/omega/Agentik_Engine/omega_engine/menu.py +1065 -0
  183. package/omega/Agentik_Engine/omega_engine/migrations/__init__.py +144 -0
  184. package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
  185. package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/v0_14_0.cpython-313.pyc +0 -0
  186. package/omega/Agentik_Engine/omega_engine/migrations/v0_14_0.py +29 -0
  187. package/omega/Agentik_Engine/omega_engine/mission.py +29 -14
  188. package/omega/Agentik_Engine/omega_engine/plan.py +846 -0
  189. package/omega/Agentik_Engine/omega_engine/prompts.py +158 -0
  190. package/omega/Agentik_Engine/omega_engine/provider.py +408 -13
  191. package/omega/Agentik_Engine/omega_engine/prune.py +151 -0
  192. package/omega/Agentik_Engine/omega_engine/pursue.py +205 -0
  193. package/omega/Agentik_Engine/omega_engine/rag/__init__.py +21 -0
  194. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/__init__.cpython-313.pyc +0 -0
  195. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/agentic.cpython-313.pyc +0 -0
  196. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/base.cpython-313.pyc +0 -0
  197. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/corrective.cpython-313.pyc +0 -0
  198. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/graph.cpython-313.pyc +0 -0
  199. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/hybrid.cpython-313.pyc +0 -0
  200. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/multimodal.cpython-313.pyc +0 -0
  201. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/router.cpython-313.pyc +0 -0
  202. package/omega/Agentik_Engine/omega_engine/rag/agentic.py +83 -0
  203. package/omega/Agentik_Engine/omega_engine/rag/base.py +42 -0
  204. package/omega/Agentik_Engine/omega_engine/rag/corrective.py +119 -0
  205. package/omega/Agentik_Engine/omega_engine/rag/graph.py +169 -0
  206. package/omega/Agentik_Engine/omega_engine/rag/hybrid.py +205 -0
  207. package/omega/Agentik_Engine/omega_engine/rag/multimodal.py +136 -0
  208. package/omega/Agentik_Engine/omega_engine/rag/router.py +110 -0
  209. package/omega/Agentik_Engine/omega_engine/reducer.py +21 -3
  210. package/omega/Agentik_Engine/omega_engine/router.py +28 -0
  211. package/omega/Agentik_Engine/omega_engine/skill_discovery/__init__.py +48 -0
  212. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/__init__.cpython-313.pyc +0 -0
  213. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/auditor.cpython-313.pyc +0 -0
  214. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/finder.cpython-313.pyc +0 -0
  215. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/installer.cpython-313.pyc +0 -0
  216. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/marketplaces.cpython-313.pyc +0 -0
  217. package/omega/Agentik_Engine/omega_engine/skill_discovery/auditor.py +232 -0
  218. package/omega/Agentik_Engine/omega_engine/skill_discovery/finder.py +94 -0
  219. package/omega/Agentik_Engine/omega_engine/skill_discovery/installer.py +129 -0
  220. package/omega/Agentik_Engine/omega_engine/skill_discovery/marketplaces.py +80 -0
  221. package/omega/Agentik_Engine/omega_engine/skill_routing.py +388 -0
  222. package/omega/Agentik_Engine/omega_engine/smoke.py +81 -0
  223. package/omega/Agentik_Engine/omega_engine/store.py +132 -25
  224. package/omega/Agentik_Engine/omega_engine/sync.py +445 -0
  225. package/omega/Agentik_Engine/omega_engine/telegram_history.py +260 -0
  226. package/omega/Agentik_Engine/omega_engine/tmux.py +526 -0
  227. package/omega/Agentik_Engine/omega_engine/tools.py +272 -0
  228. package/omega/Agentik_Engine/omega_engine/understand_anything.py +275 -0
  229. package/omega/Agentik_Engine/omega_engine/updater.py +70 -0
  230. package/omega/Agentik_Engine/omega_engine/validate.py +186 -0
  231. package/omega/Agentik_Engine/omega_engine/vault.py +342 -0
  232. package/omega/Agentik_Engine/omega_engine/webhooks.py +262 -0
  233. package/omega/Agentik_Engine/omega_engine/worker.py +526 -0
  234. package/omega/Agentik_Engine/pyproject.toml +1 -1
  235. package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313-pytest-8.4.2.pyc +0 -0
  236. package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313.pyc +0 -0
  237. package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313-pytest-8.4.2.pyc +0 -0
  238. package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313.pyc +0 -0
  239. package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313-pytest-8.4.2.pyc +0 -0
  240. package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313.pyc +0 -0
  241. package/omega/Agentik_Engine/tests/__pycache__/test_audit_arsenal.cpython-313-pytest-8.4.2.pyc +0 -0
  242. package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313-pytest-8.4.2.pyc +0 -0
  243. package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313.pyc +0 -0
  244. package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313-pytest-8.4.2.pyc +0 -0
  245. package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313.pyc +0 -0
  246. package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313-pytest-8.4.2.pyc +0 -0
  247. package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313.pyc +0 -0
  248. package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313-pytest-8.4.2.pyc +0 -0
  249. package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313.pyc +0 -0
  250. package/omega/Agentik_Engine/tests/__pycache__/test_executor.cpython-313-pytest-8.4.2.pyc +0 -0
  251. package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313-pytest-8.4.2.pyc +0 -0
  252. package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313.pyc +0 -0
  253. package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313-pytest-8.4.2.pyc +0 -0
  254. package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313.pyc +0 -0
  255. package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313-pytest-8.4.2.pyc +0 -0
  256. package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313.pyc +0 -0
  257. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313-pytest-8.4.2.pyc +0 -0
  258. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313.pyc +0 -0
  259. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313-pytest-8.4.2.pyc +0 -0
  260. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313.pyc +0 -0
  261. package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313-pytest-8.4.2.pyc +0 -0
  262. package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313.pyc +0 -0
  263. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313-pytest-8.4.2.pyc +0 -0
  264. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313.pyc +0 -0
  265. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
  266. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
  267. package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313-pytest-8.4.2.pyc +0 -0
  268. package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313.pyc +0 -0
  269. package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313-pytest-8.4.2.pyc +0 -0
  270. package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313.pyc +0 -0
  271. package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313-pytest-8.4.2.pyc +0 -0
  272. package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313.pyc +0 -0
  273. package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313-pytest-8.4.2.pyc +0 -0
  274. package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313.pyc +0 -0
  275. package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313-pytest-8.4.2.pyc +0 -0
  276. package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313.pyc +0 -0
  277. package/omega/Agentik_Engine/tests/__pycache__/test_mission.cpython-313-pytest-8.4.2.pyc +0 -0
  278. package/omega/Agentik_Engine/tests/__pycache__/test_progress.cpython-313-pytest-8.4.2.pyc +0 -0
  279. package/omega/Agentik_Engine/tests/__pycache__/test_project.cpython-313-pytest-8.4.2.pyc +0 -0
  280. package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313-pytest-8.4.2.pyc +0 -0
  281. package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313.pyc +0 -0
  282. package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313-pytest-8.4.2.pyc +0 -0
  283. package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313.pyc +0 -0
  284. package/omega/Agentik_Engine/tests/__pycache__/test_reducer.cpython-313-pytest-8.4.2.pyc +0 -0
  285. package/omega/Agentik_Engine/tests/__pycache__/test_report.cpython-313-pytest-8.4.2.pyc +0 -0
  286. package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313-pytest-8.4.2.pyc +0 -0
  287. package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313.pyc +0 -0
  288. package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313-pytest-8.4.2.pyc +0 -0
  289. package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313.pyc +0 -0
  290. package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313-pytest-8.4.2.pyc +0 -0
  291. package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313.pyc +0 -0
  292. package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313-pytest-8.4.2.pyc +0 -0
  293. package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313.pyc +0 -0
  294. package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313-pytest-8.4.2.pyc +0 -0
  295. package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313.pyc +0 -0
  296. package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313-pytest-8.4.2.pyc +0 -0
  297. package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313.pyc +0 -0
  298. package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc +0 -0
  299. package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313.pyc +0 -0
  300. package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313-pytest-8.4.2.pyc +0 -0
  301. package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313.pyc +0 -0
  302. package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313-pytest-8.4.2.pyc +0 -0
  303. package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313.pyc +0 -0
  304. package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313-pytest-8.4.2.pyc +0 -0
  305. package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313.pyc +0 -0
  306. package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313-pytest-8.4.2.pyc +0 -0
  307. package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313.pyc +0 -0
  308. package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313-pytest-8.4.2.pyc +0 -0
  309. package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313.pyc +0 -0
  310. package/omega/Agentik_Engine/tests/test_account.py +338 -0
  311. package/omega/Agentik_Engine/tests/test_adversarial.py +351 -0
  312. package/omega/Agentik_Engine/tests/test_agents_envelope.py +274 -0
  313. package/omega/Agentik_Engine/tests/test_audits_pipeline.py +348 -0
  314. package/omega/Agentik_Engine/tests/test_auto_update_and_migrations.py +394 -0
  315. package/omega/Agentik_Engine/tests/test_autonomous.py +361 -0
  316. package/omega/Agentik_Engine/tests/test_educators.py +233 -0
  317. package/omega/Agentik_Engine/tests/test_genesis_and_plan.py +573 -0
  318. package/omega/Agentik_Engine/tests/test_graphify.py +190 -0
  319. package/omega/Agentik_Engine/tests/test_handoff.py +311 -0
  320. package/omega/Agentik_Engine/tests/test_hermes_and_ua.py +387 -0
  321. package/omega/Agentik_Engine/tests/test_hermes_bootstrap_and_desktop.py +358 -0
  322. package/omega/Agentik_Engine/tests/test_install_steps.py +359 -0
  323. package/omega/Agentik_Engine/tests/test_install_ux.py +151 -0
  324. package/omega/Agentik_Engine/tests/test_installer_wiring.py +496 -0
  325. package/omega/Agentik_Engine/tests/test_intelligence.py +285 -0
  326. package/omega/Agentik_Engine/tests/test_llm_clis_and_uninstall.py +228 -0
  327. package/omega/Agentik_Engine/tests/test_managed_agent.py +363 -0
  328. package/omega/Agentik_Engine/tests/test_max_provider_and_menu.py +231 -0
  329. package/omega/Agentik_Engine/tests/test_menu_coverage.py +72 -0
  330. package/omega/Agentik_Engine/tests/test_pursue_cadence.py +217 -0
  331. package/omega/Agentik_Engine/tests/test_rag.py +287 -0
  332. package/omega/Agentik_Engine/tests/test_role_aliases_and_ssot.py +207 -0
  333. package/omega/Agentik_Engine/tests/test_skill_discovery_and_gate.py +337 -0
  334. package/omega/Agentik_Engine/tests/test_skill_power.py +259 -0
  335. package/omega/Agentik_Engine/tests/test_skill_routing.py +189 -0
  336. package/omega/Agentik_Engine/tests/test_snapshot_partial.py +172 -0
  337. package/omega/Agentik_Engine/tests/test_telegram_history.py +209 -0
  338. package/omega/Agentik_Engine/tests/test_tmux_and_aisb_chat.py +223 -0
  339. package/omega/Agentik_Engine/tests/test_tools_and_sync.py +312 -0
  340. package/omega/Agentik_Engine/tests/test_v06_features.py +370 -0
  341. package/omega/Agentik_Engine/tests/test_vault.py +173 -0
  342. package/omega/Agentik_Engine/tests/test_webhooks_and_readiness.py +277 -0
  343. package/omega/Agentik_Engine/tests/test_worker_and_cleanup.py +541 -0
  344. package/omega/Agentik_Extra/etc/secrets/.vault-key +3 -0
  345. package/omega/Agentik_Extra/etc/secrets/.vault-pub +1 -0
  346. package/omega/Agentik_Runtime/audits.db +0 -0
  347. package/omega/Agentik_SSOT/VERSION +1 -1
  348. package/omega/Agentik_SSOT/claude-plugins/claude-plugins.yaml +100 -0
  349. package/omega/Agentik_SSOT/docs/LAYERS.md +90 -0
  350. package/omega/Agentik_SSOT/docs/USER-JOURNEY.md +283 -0
  351. package/omega/Agentik_SSOT/marketplaces/design-discipline.yaml +86 -0
  352. package/omega/Agentik_SSOT/skills/a11yaudit/SKILL.md +161 -0
  353. package/omega/Agentik_SSOT/skills/apiaudit/SKILL.md +157 -0
  354. package/omega/Agentik_SSOT/skills/automationaudit/SKILL.md +161 -0
  355. package/omega/Agentik_SSOT/skills/cadence/SKILL.md +76 -0
  356. package/omega/Agentik_SSOT/skills/codeaudit/SKILL.md +153 -0
  357. package/omega/Agentik_SSOT/skills/copyaudit/SKILL.md +161 -0
  358. package/omega/Agentik_SSOT/skills/dataaudit/SKILL.md +157 -0
  359. package/omega/Agentik_SSOT/skills/debugaudit/SKILL.md +161 -0
  360. package/omega/Agentik_SSOT/skills/dispatch/SKILL.md +79 -0
  361. package/omega/Agentik_SSOT/skills/dxaudit/SKILL.md +161 -0
  362. package/omega/Agentik_SSOT/skills/featureaudit/SKILL.md +161 -0
  363. package/omega/Agentik_SSOT/skills/flowaudit/SKILL.md +165 -0
  364. package/omega/Agentik_SSOT/skills/genesis/SKILL.md +116 -0
  365. package/omega/Agentik_SSOT/skills/handoff/SKILL.md +117 -0
  366. package/omega/Agentik_SSOT/skills/logicaudit/SKILL.md +165 -0
  367. package/omega/Agentik_SSOT/skills/motionaudit/SKILL.md +165 -0
  368. package/omega/Agentik_SSOT/skills/perfaudit/SKILL.md +161 -0
  369. package/omega/Agentik_SSOT/skills/plan/SKILL.md +127 -0
  370. package/omega/Agentik_SSOT/skills/pursue/SKILL.md +68 -0
  371. package/omega/Agentik_SSOT/skills/rag-route.md +82 -0
  372. package/omega/Agentik_SSOT/skills/refontaudit/SKILL.md +165 -0
  373. package/omega/Agentik_SSOT/skills/retentionaudit/SKILL.md +165 -0
  374. package/omega/Agentik_SSOT/skills/secaudit/SKILL.md +157 -0
  375. package/omega/Agentik_SSOT/skills/seoaudit/SKILL.md +161 -0
  376. package/omega/Agentik_SSOT/skills/skill-auditor/SKILL.md +83 -0
  377. package/omega/Agentik_SSOT/skills/skill-finder/SKILL.md +116 -0
  378. package/omega/Agentik_SSOT/skills/uiuxaudit/SKILL.md +165 -0
  379. package/package.json +2 -2
@@ -0,0 +1,186 @@
1
+ """Validate provider credentials at install time + on demand.
2
+
3
+ The install used to write provider config + credentials to the vault and
4
+ then say "ok". The first mission would discover the key was wrong/expired
5
+ and fail. This module closes the gap: ``omega validate providers`` does a
6
+ 1-token round-trip against each configured provider's real API.
7
+
8
+ For each provider we know how to validate:
9
+
10
+ * **claude** ``/v1/messages`` with max_tokens=1
11
+ * **glm** ``/api/paas/v4/chat/completions`` with max_tokens=1
12
+ * **openai** ``/v1/chat/completions`` with max_tokens=1
13
+ * **deepseek** ``/v1/chat/completions`` with max_tokens=1
14
+
15
+ Each provider's adapter already knows its endpoint + auth shape — we
16
+ reuse it. A 200 OK is a pass; anything else is a fail with the API's
17
+ error string surfaced.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ from dataclasses import dataclass
23
+ from pathlib import Path
24
+ from typing import Any
25
+
26
+ import yaml
27
+
28
+
29
+ @dataclass
30
+ class ValidationResult:
31
+ provider: str
32
+ ok: bool
33
+ detail: str
34
+ elapsed_ms: int = 0
35
+
36
+
37
+ def _read_router_config(omega_home: str | Path) -> list[dict[str, Any]]:
38
+ p = (Path(omega_home) / "Agentik_SSOT" / "providers" / "router.yaml")
39
+ if not p.exists():
40
+ return []
41
+ data = yaml.safe_load(p.read_text()) or {}
42
+ return list(data.get("providers") or [])
43
+
44
+
45
+ def _read_secret(omega_home: str | Path, secret_ref: str) -> str:
46
+ if not secret_ref:
47
+ return ""
48
+ if env := os.environ.get(secret_ref):
49
+ return env
50
+ try:
51
+ from omega_engine.vault import vault_read
52
+ return vault_read(omega_home, secret_ref) or ""
53
+ except Exception: # noqa: BLE001
54
+ return ""
55
+
56
+
57
+ def _validate_claude(api_key: str) -> ValidationResult:
58
+ """1-token call against Anthropic /v1/messages."""
59
+ if not api_key:
60
+ return ValidationResult("claude", False, "no API key in env or vault")
61
+ import json
62
+ import time
63
+ import urllib.error
64
+ import urllib.request
65
+ start = time.time()
66
+ body = json.dumps({
67
+ "model": "claude-haiku-4-5",
68
+ "max_tokens": 1,
69
+ "messages": [{"role": "user", "content": "x"}],
70
+ }).encode()
71
+ req = urllib.request.Request(
72
+ "https://api.anthropic.com/v1/messages",
73
+ data=body, method="POST",
74
+ headers={
75
+ "Content-Type": "application/json",
76
+ "x-api-key": api_key,
77
+ "anthropic-version": "2023-06-01",
78
+ },
79
+ )
80
+ try:
81
+ with urllib.request.urlopen(req, timeout=15) as resp: # noqa: S310
82
+ resp.read()
83
+ return ValidationResult(
84
+ "claude", True, "ok", int((time.time() - start) * 1000),
85
+ )
86
+ except urllib.error.HTTPError as exc:
87
+ detail = exc.read().decode("utf-8", errors="replace")[:300] if exc.fp else ""
88
+ return ValidationResult(
89
+ "claude", False, f"HTTP {exc.code}: {detail or exc.reason}",
90
+ int((time.time() - start) * 1000),
91
+ )
92
+ except Exception as exc: # noqa: BLE001
93
+ return ValidationResult(
94
+ "claude", False, f"network error: {exc}"[:200],
95
+ int((time.time() - start) * 1000),
96
+ )
97
+
98
+
99
+ def _validate_openai_compat(
100
+ provider: str, api_key: str, base_url: str, model: str,
101
+ ) -> ValidationResult:
102
+ """1-token call against any OpenAI-compatible /chat/completions."""
103
+ if not api_key:
104
+ return ValidationResult(provider, False, "no API key in env or vault")
105
+ import json
106
+ import time
107
+ import urllib.error
108
+ import urllib.request
109
+ start = time.time()
110
+ body = json.dumps({
111
+ "model": model,
112
+ "max_tokens": 1,
113
+ "messages": [{"role": "user", "content": "x"}],
114
+ }).encode()
115
+ req = urllib.request.Request(
116
+ f"{base_url.rstrip('/')}/chat/completions",
117
+ data=body, method="POST",
118
+ headers={
119
+ "Content-Type": "application/json",
120
+ "Authorization": f"Bearer {api_key}",
121
+ },
122
+ )
123
+ try:
124
+ with urllib.request.urlopen(req, timeout=15) as resp: # noqa: S310
125
+ resp.read()
126
+ return ValidationResult(
127
+ provider, True, "ok", int((time.time() - start) * 1000),
128
+ )
129
+ except urllib.error.HTTPError as exc:
130
+ detail = exc.read().decode("utf-8", errors="replace")[:300] if exc.fp else ""
131
+ return ValidationResult(
132
+ provider, False, f"HTTP {exc.code}: {detail or exc.reason}",
133
+ int((time.time() - start) * 1000),
134
+ )
135
+ except Exception as exc: # noqa: BLE001
136
+ return ValidationResult(
137
+ provider, False, f"network error: {exc}"[:200],
138
+ int((time.time() - start) * 1000),
139
+ )
140
+
141
+
142
+ def validate_one(
143
+ omega_home: str | Path, provider_id: str,
144
+ *, secret_ref: str | None = None,
145
+ ) -> ValidationResult:
146
+ """Validate one provider by id. Reads secret from env or vault."""
147
+ secret_ref = (
148
+ secret_ref or f"{provider_id.upper()}_API_KEY"
149
+ )
150
+ key = _read_secret(omega_home, secret_ref)
151
+ pid = provider_id.lower()
152
+ if pid == "claude":
153
+ return _validate_claude(key)
154
+ if pid == "glm":
155
+ return _validate_openai_compat(
156
+ "glm", key,
157
+ "https://open.bigmodel.cn/api/paas/v4",
158
+ "glm-4-flash",
159
+ )
160
+ if pid == "openai":
161
+ return _validate_openai_compat(
162
+ "openai", key, "https://api.openai.com/v1", "gpt-4o-mini",
163
+ )
164
+ if pid == "deepseek":
165
+ return _validate_openai_compat(
166
+ "deepseek", key, "https://api.deepseek.com/v1", "deepseek-chat",
167
+ )
168
+ return ValidationResult(
169
+ provider_id, False,
170
+ f"no validator known for provider {provider_id!r}",
171
+ )
172
+
173
+
174
+ def validate_all(omega_home: str | Path) -> list[ValidationResult]:
175
+ """Validate every provider in ``router.yaml``."""
176
+ out: list[ValidationResult] = []
177
+ for cfg in _read_router_config(omega_home):
178
+ if not bool(cfg.get("enabled", True)):
179
+ continue
180
+ pid = str(cfg.get("id", "")).strip()
181
+ if not pid:
182
+ continue
183
+ out.append(validate_one(
184
+ omega_home, pid, secret_ref=cfg.get("secret_ref"),
185
+ ))
186
+ return out
@@ -0,0 +1,342 @@
1
+ """Encrypted secrets vault — age if available, chmod 600 plaintext fallback.
2
+
3
+ The vault is the single source for *how* a secret reaches disk. It exists so
4
+ that the rest of the engine never duplicates the "if age then encrypt else
5
+ chmod 600" logic.
6
+
7
+ Backends
8
+ --------
9
+ * **age** (preferred) — uses the system `age` and `age-keygen` binaries.
10
+ At first write, we generate an age identity at
11
+ `Agentik_Extra/etc/secrets/.vault-key` (mode 600) plus its public recipient
12
+ at `.vault-pub` (mode 644). Every secret is written as `<ref>.age` next to
13
+ the key file.
14
+ * **plain** (fallback) — when age is not installed, secrets are still stored
15
+ with mode 600 under `Agentik_Extra/etc/secrets/<ref>`. The doctor flags
16
+ this as "unencrypted" so the operator can install age and re-write.
17
+
18
+ API
19
+ ---
20
+ * ``vault_init(home)`` — idempotent. Ensures the secrets dir exists, mode 700.
21
+ If age is installed, ensures the keypair exists. Returns a status dict.
22
+ * ``vault_write(home, ref, value)`` — write a secret. Returns the path.
23
+ * ``vault_read(home, ref) -> str | None`` — read a secret, or None if absent.
24
+ * ``vault_status(home)`` — summary for ``omega doctor``.
25
+
26
+ The vault is intentionally narrow: one value per ref, plain string. Tokens
27
+ are strings; structured secrets (mcp-<id>.env) keep their dotenv layout for
28
+ now and can migrate later.
29
+ """
30
+ from __future__ import annotations
31
+
32
+ import os
33
+ import shutil
34
+ import stat
35
+ import subprocess
36
+ from pathlib import Path
37
+ from typing import Any
38
+
39
+
40
+ def _safe_ref(ref: str) -> str:
41
+ """Refuse path-traversal — `ref` is a flat filename only."""
42
+ if not ref or "/" in ref or "\\" in ref or ".." in ref:
43
+ raise ValueError(f"invalid secret ref: {ref!r}")
44
+ if ref.startswith("."):
45
+ raise ValueError(f"secret ref must not start with '.': {ref!r}")
46
+ return ref
47
+
48
+
49
+ def _safe_project_slug(project: str) -> str:
50
+ """A project slug is a flat name — no traversal."""
51
+ if not project or "/" in project or "\\" in project or ".." in project:
52
+ raise ValueError(f"invalid project slug: {project!r}")
53
+ if project.startswith("."):
54
+ raise ValueError(f"project slug must not start with '.': {project!r}")
55
+ return project
56
+
57
+
58
+ def _secrets_dir(home: Path, project: str | None = None) -> Path:
59
+ """Return the vault directory, creating it with mode 700.
60
+
61
+ When ``project`` is set, the directory is
62
+ ``Agentik_Coding/projects/<slug>/.secrets/`` — a per-project vault that
63
+ sits next to the project's code. Otherwise the global vault at
64
+ ``Agentik_Extra/etc/secrets/``.
65
+ """
66
+ if project is not None:
67
+ slug = _safe_project_slug(project)
68
+ d = (Path(home) / "Agentik_Coding" / "projects" / slug / ".secrets")
69
+ else:
70
+ d = Path(home) / "Agentik_Extra" / "etc" / "secrets"
71
+ d.mkdir(parents=True, exist_ok=True)
72
+ try:
73
+ os.chmod(d, 0o700)
74
+ except OSError:
75
+ pass
76
+ return d
77
+
78
+
79
+ def _global_key_dir(home: Path) -> Path:
80
+ """Where the age keypair lives. Always the global secrets dir — every
81
+ per-project vault encrypts to the same recipient so the engine can
82
+ decrypt anything with one key."""
83
+ return _secrets_dir(home, project=None)
84
+
85
+
86
+ def _age_available() -> bool:
87
+ return shutil.which("age") is not None and shutil.which("age-keygen") is not None
88
+
89
+
90
+ def vault_init(home: str | Path) -> dict[str, Any]:
91
+ """Idempotent vault setup. Generates the age keypair on first call when
92
+ age is installed; no-op afterwards. Always safe to call.
93
+
94
+ Returns: ``{ "backend": "age" | "plain", "key_path"?, "pub_path"?, ... }``
95
+ """
96
+ home = Path(home)
97
+ sec = _global_key_dir(home)
98
+ if not _age_available():
99
+ return {"backend": "plain", "age_installed": False, "secrets_dir": str(sec)}
100
+
101
+ key_path = sec / ".vault-key"
102
+ pub_path = sec / ".vault-pub"
103
+ if key_path.exists() and pub_path.exists():
104
+ return {
105
+ "backend": "age", "age_installed": True,
106
+ "key_path": str(key_path), "pub_path": str(pub_path),
107
+ "secrets_dir": str(sec),
108
+ }
109
+
110
+ # Generate a fresh keypair.
111
+ proc = subprocess.run(
112
+ ["age-keygen", "-o", str(key_path)],
113
+ check=False, capture_output=True, text=True, timeout=30,
114
+ )
115
+ if proc.returncode != 0:
116
+ return {
117
+ "backend": "plain", "age_installed": True,
118
+ "secrets_dir": str(sec),
119
+ "warning": f"age-keygen failed: {proc.stderr.strip()[:200]}",
120
+ }
121
+ try:
122
+ os.chmod(key_path, 0o600)
123
+ except OSError:
124
+ pass
125
+
126
+ # age-keygen prints "Public key: age1xyz..." to stderr.
127
+ pub: str | None = None
128
+ for line in (proc.stderr or "").splitlines():
129
+ s = line.strip()
130
+ if s.startswith("Public key:"):
131
+ pub = s.split(":", 1)[1].strip()
132
+ break
133
+ if not pub:
134
+ # Some age-keygen versions write the public key as a comment in the
135
+ # private-key file. Try to extract it from there.
136
+ for line in key_path.read_text().splitlines():
137
+ s = line.strip()
138
+ if s.lower().startswith("# public key:"):
139
+ pub = s.split(":", 1)[1].strip()
140
+ break
141
+ if not pub:
142
+ return {
143
+ "backend": "plain", "age_installed": True,
144
+ "secrets_dir": str(sec),
145
+ "warning": "could not extract public key from age-keygen output",
146
+ }
147
+
148
+ pub_path.write_text(pub + "\n")
149
+ try:
150
+ os.chmod(pub_path, 0o644)
151
+ except OSError:
152
+ pass
153
+ return {
154
+ "backend": "age", "age_installed": True,
155
+ "key_path": str(key_path), "pub_path": str(pub_path),
156
+ "secrets_dir": str(sec),
157
+ }
158
+
159
+
160
+ def vault_write(
161
+ home: str | Path,
162
+ ref: str,
163
+ value: str,
164
+ *,
165
+ project: str | None = None,
166
+ ) -> Path:
167
+ """Write a secret. age-encrypted when available, mode-600 plaintext otherwise.
168
+
169
+ With ``project=<slug>``, the secret is scoped to that project's vault at
170
+ ``Agentik_Coding/projects/<slug>/.secrets/`` (encrypted to the SAME age
171
+ recipient as the global vault, so one key decrypts everything). Without
172
+ ``project``, the global vault is used.
173
+
174
+ Returns the on-disk path written. Idempotent for a given (ref, value):
175
+ re-writing rotates the secret in place.
176
+ """
177
+ ref = _safe_ref(ref)
178
+ if not isinstance(value, str) or not value:
179
+ raise ValueError("secret value must be a non-empty string")
180
+
181
+ home = Path(home)
182
+ # vault_init always operates on the global dir so the keypair is
183
+ # discoverable from any project.
184
+ info = vault_init(home)
185
+ sec = _secrets_dir(home, project=project)
186
+
187
+ if info["backend"] == "age":
188
+ pub = Path(info["pub_path"]).read_text().strip()
189
+ target = sec / f"{ref}.age"
190
+ proc = subprocess.run(
191
+ ["age", "-r", pub, "-o", str(target)],
192
+ input=value, text=True, check=False, capture_output=True, timeout=30,
193
+ )
194
+ if proc.returncode != 0:
195
+ raise RuntimeError(
196
+ f"age encrypt failed for ref={ref}: {proc.stderr.strip()[:200]}"
197
+ )
198
+ try:
199
+ os.chmod(target, 0o600)
200
+ except OSError:
201
+ pass
202
+ # Best-effort cleanup of a previous plaintext for the same ref.
203
+ plain_old = sec / ref
204
+ if plain_old.exists():
205
+ try:
206
+ plain_old.unlink()
207
+ except OSError:
208
+ pass
209
+ return target
210
+
211
+ # Plain fallback.
212
+ target = sec / ref
213
+ fd = os.open(str(target), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
214
+ try:
215
+ with os.fdopen(fd, "w") as fh:
216
+ fh.write(value)
217
+ finally:
218
+ try:
219
+ os.close(fd)
220
+ except OSError:
221
+ pass
222
+ try:
223
+ os.chmod(target, stat.S_IRUSR | stat.S_IWUSR) # 0o600
224
+ except OSError:
225
+ pass
226
+ return target
227
+
228
+
229
+ def vault_read(
230
+ home: str | Path,
231
+ ref: str,
232
+ *,
233
+ project: str | None = None,
234
+ ) -> str | None:
235
+ """Read a secret. Tries age then plain; returns None if neither exists.
236
+
237
+ ``project=<slug>`` reads from the per-project vault.
238
+
239
+ Raises RuntimeError if an encrypted file exists but decryption fails
240
+ (so callers don't silently treat a broken vault as missing).
241
+ """
242
+ ref = _safe_ref(ref)
243
+ home = Path(home)
244
+ sec = _secrets_dir(home, project=project)
245
+ age_file = sec / f"{ref}.age"
246
+ plain_file = sec / ref
247
+
248
+ if age_file.exists():
249
+ # The age key always lives in the global dir.
250
+ key = _global_key_dir(home) / ".vault-key"
251
+ if not key.exists():
252
+ raise RuntimeError(
253
+ f"encrypted secret {age_file} exists but vault key {key} is missing"
254
+ )
255
+ if not _age_available():
256
+ raise RuntimeError(
257
+ f"encrypted secret {age_file} exists but `age` is not installed"
258
+ )
259
+ proc = subprocess.run(
260
+ ["age", "-d", "-i", str(key), str(age_file)],
261
+ check=False, capture_output=True, text=True, timeout=30,
262
+ )
263
+ if proc.returncode != 0:
264
+ raise RuntimeError(
265
+ f"age decrypt failed for ref={ref}: {proc.stderr.strip()[:200]}"
266
+ )
267
+ return proc.stdout
268
+ if plain_file.exists():
269
+ return plain_file.read_text()
270
+ return None
271
+
272
+
273
+ def _file_has_real_secret(path: Path) -> bool:
274
+ """True iff the file contains at least one non-comment KEY=VALUE line.
275
+
276
+ A file with only commented placeholders like
277
+ # COMPOSIO_API_KEY= # set this when ready
278
+ has nothing to leak and is not counted as a plaintext secret.
279
+ """
280
+ try:
281
+ text = path.read_text(errors="replace")
282
+ except OSError:
283
+ return False
284
+ if not text.strip():
285
+ return False
286
+ for raw in text.splitlines():
287
+ ln = raw.strip()
288
+ if not ln or ln.startswith("#"):
289
+ continue
290
+ if "=" not in ln:
291
+ # A bare opaque value (e.g. an OAuth token written as the whole
292
+ # file body) still counts as a real secret.
293
+ return True
294
+ k, _, v = ln.partition("=")
295
+ if v.strip().strip('"').strip("'"):
296
+ return True
297
+ return False
298
+
299
+
300
+ def vault_status(home: str | Path, *, project: str | None = None) -> dict[str, Any]:
301
+ """Summary of the vault for `omega doctor`.
302
+
303
+ With ``project=<slug>``, summarises the per-project vault only.
304
+ """
305
+ home = Path(home)
306
+ sec = _secrets_dir(home, project=project)
307
+ info = vault_init(home)
308
+ files = [p for p in sec.iterdir() if p.is_file() and not p.name.startswith(".")]
309
+ encrypted = [p.name for p in files if p.suffix == ".age"]
310
+ plain_files = [p for p in files if p.suffix != ".age"]
311
+ # Discriminate real plaintext secrets from placeholder dotenv files.
312
+ real_plain = [p.name for p in plain_files if _file_has_real_secret(p)]
313
+ placeholders = [p.name for p in plain_files if p.name not in real_plain]
314
+ return {
315
+ "backend": info["backend"],
316
+ "age_installed": info.get("age_installed", False),
317
+ "encrypted_count": len(encrypted),
318
+ "plaintext_count": len(real_plain),
319
+ "placeholder_count": len(placeholders),
320
+ "secrets_dir": str(sec),
321
+ "project": project,
322
+ }
323
+
324
+
325
+ def list_project_vaults(home: str | Path) -> list[dict[str, Any]]:
326
+ """Enumerate every per-project vault under
327
+ ``Agentik_Coding/projects/<slug>/.secrets/``. Returns a list of vault
328
+ statuses, one per project that has any vault (encrypted or plain).
329
+ """
330
+ home = Path(home)
331
+ projects_root = home / "Agentik_Coding" / "projects"
332
+ out: list[dict[str, Any]] = []
333
+ if not projects_root.is_dir():
334
+ return out
335
+ for proj_dir in sorted(projects_root.iterdir()):
336
+ if not proj_dir.is_dir():
337
+ continue
338
+ sec = proj_dir / ".secrets"
339
+ if not sec.is_dir():
340
+ continue
341
+ out.append(vault_status(home, project=proj_dir.name))
342
+ return out