@agentikos/omega-os 0.2.0 → 0.19.6

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 (376) hide show
  1. package/README.md +33 -3
  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 +790 -21
  10. package/bootstrap/manifest.example.yaml +87 -1
  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/GAP-ANALYSIS.md +214 -0
  48. package/docs/INSTALL.md +47 -9
  49. package/docs/MCP-AND-PLUGINS.md +31 -4
  50. package/docs/SIMULATION.md +171 -0
  51. package/docs/simulate.sh +211 -0
  52. package/install.sh +164 -17
  53. package/omega/Agentik_Engine/README.md +4 -2
  54. package/omega/Agentik_Engine/omega_engine/__init__.py +147 -1
  55. package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
  56. package/omega/Agentik_Engine/omega_engine/__pycache__/account.cpython-313.pyc +0 -0
  57. package/omega/Agentik_Engine/omega_engine/__pycache__/agent_messages.cpython-313.pyc +0 -0
  58. package/omega/Agentik_Engine/omega_engine/__pycache__/aisb_chat.cpython-313.pyc +0 -0
  59. package/omega/Agentik_Engine/omega_engine/__pycache__/audit.cpython-313.pyc +0 -0
  60. package/omega/Agentik_Engine/omega_engine/__pycache__/audit_arsenal.cpython-313.pyc +0 -0
  61. package/omega/Agentik_Engine/omega_engine/__pycache__/audit_diff.cpython-313.pyc +0 -0
  62. package/omega/Agentik_Engine/omega_engine/__pycache__/audit_gate.cpython-313.pyc +0 -0
  63. package/omega/Agentik_Engine/omega_engine/__pycache__/auto_update.cpython-313.pyc +0 -0
  64. package/omega/Agentik_Engine/omega_engine/__pycache__/autonomous.cpython-313.pyc +0 -0
  65. package/omega/Agentik_Engine/omega_engine/__pycache__/backup.cpython-313.pyc +0 -0
  66. package/omega/Agentik_Engine/omega_engine/__pycache__/barrier.cpython-313.pyc +0 -0
  67. package/omega/Agentik_Engine/omega_engine/__pycache__/bus.cpython-313.pyc +0 -0
  68. package/omega/Agentik_Engine/omega_engine/__pycache__/cadence.cpython-313.pyc +0 -0
  69. package/omega/Agentik_Engine/omega_engine/__pycache__/classifier.cpython-313.pyc +0 -0
  70. package/omega/Agentik_Engine/omega_engine/__pycache__/cleanup.cpython-313.pyc +0 -0
  71. package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
  72. package/omega/Agentik_Engine/omega_engine/__pycache__/completions.cpython-313.pyc +0 -0
  73. package/omega/Agentik_Engine/omega_engine/__pycache__/costs.cpython-313.pyc +0 -0
  74. package/omega/Agentik_Engine/omega_engine/__pycache__/done_signal.cpython-313.pyc +0 -0
  75. package/omega/Agentik_Engine/omega_engine/__pycache__/envelope.cpython-313.pyc +0 -0
  76. package/omega/Agentik_Engine/omega_engine/__pycache__/events.cpython-313.pyc +0 -0
  77. package/omega/Agentik_Engine/omega_engine/__pycache__/executor.cpython-313.pyc +0 -0
  78. package/omega/Agentik_Engine/omega_engine/__pycache__/handoff.cpython-313.pyc +0 -0
  79. package/omega/Agentik_Engine/omega_engine/__pycache__/hermes.cpython-313.pyc +0 -0
  80. package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_bootstrap.cpython-313.pyc +0 -0
  81. package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_desktop.cpython-313.pyc +0 -0
  82. package/omega/Agentik_Engine/omega_engine/__pycache__/learning.cpython-313.pyc +0 -0
  83. package/omega/Agentik_Engine/omega_engine/__pycache__/managed_agent.cpython-313.pyc +0 -0
  84. package/omega/Agentik_Engine/omega_engine/__pycache__/memory.cpython-313.pyc +0 -0
  85. package/omega/Agentik_Engine/omega_engine/__pycache__/menu.cpython-313.pyc +0 -0
  86. package/omega/Agentik_Engine/omega_engine/__pycache__/mission.cpython-313.pyc +0 -0
  87. package/omega/Agentik_Engine/omega_engine/__pycache__/plan.cpython-313.pyc +0 -0
  88. package/omega/Agentik_Engine/omega_engine/__pycache__/progress.cpython-313.pyc +0 -0
  89. package/omega/Agentik_Engine/omega_engine/__pycache__/project.cpython-313.pyc +0 -0
  90. package/omega/Agentik_Engine/omega_engine/__pycache__/prompts.cpython-313.pyc +0 -0
  91. package/omega/Agentik_Engine/omega_engine/__pycache__/provider.cpython-313.pyc +0 -0
  92. package/omega/Agentik_Engine/omega_engine/__pycache__/prune.cpython-313.pyc +0 -0
  93. package/omega/Agentik_Engine/omega_engine/__pycache__/pursue.cpython-313.pyc +0 -0
  94. package/omega/Agentik_Engine/omega_engine/__pycache__/reducer.cpython-313.pyc +0 -0
  95. package/omega/Agentik_Engine/omega_engine/__pycache__/report.cpython-313.pyc +0 -0
  96. package/omega/Agentik_Engine/omega_engine/__pycache__/router.cpython-313.pyc +0 -0
  97. package/omega/Agentik_Engine/omega_engine/__pycache__/skill_routing.cpython-313.pyc +0 -0
  98. package/omega/Agentik_Engine/omega_engine/__pycache__/smoke.cpython-313.pyc +0 -0
  99. package/omega/Agentik_Engine/omega_engine/__pycache__/store.cpython-313.pyc +0 -0
  100. package/omega/Agentik_Engine/omega_engine/__pycache__/supervisor.cpython-313.pyc +0 -0
  101. package/omega/Agentik_Engine/omega_engine/__pycache__/sync.cpython-313.pyc +0 -0
  102. package/omega/Agentik_Engine/omega_engine/__pycache__/task.cpython-313.pyc +0 -0
  103. package/omega/Agentik_Engine/omega_engine/__pycache__/telegram.cpython-313.pyc +0 -0
  104. package/omega/Agentik_Engine/omega_engine/__pycache__/telegram_history.cpython-313.pyc +0 -0
  105. package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
  106. package/omega/Agentik_Engine/omega_engine/__pycache__/tools.cpython-313.pyc +0 -0
  107. package/omega/Agentik_Engine/omega_engine/__pycache__/understand_anything.cpython-313.pyc +0 -0
  108. package/omega/Agentik_Engine/omega_engine/__pycache__/updater.cpython-313.pyc +0 -0
  109. package/omega/Agentik_Engine/omega_engine/__pycache__/validate.cpython-313.pyc +0 -0
  110. package/omega/Agentik_Engine/omega_engine/__pycache__/vault.cpython-313.pyc +0 -0
  111. package/omega/Agentik_Engine/omega_engine/__pycache__/webhooks.cpython-313.pyc +0 -0
  112. package/omega/Agentik_Engine/omega_engine/__pycache__/worker.cpython-313.pyc +0 -0
  113. package/omega/Agentik_Engine/omega_engine/account.py +28 -31
  114. package/omega/Agentik_Engine/omega_engine/agent_messages.py +167 -0
  115. package/omega/Agentik_Engine/omega_engine/aisb_chat.py +128 -0
  116. package/omega/Agentik_Engine/omega_engine/audit_diff.py +99 -0
  117. package/omega/Agentik_Engine/omega_engine/audit_gate.py +149 -0
  118. package/omega/Agentik_Engine/omega_engine/audits/__init__.py +60 -0
  119. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/__init__.cpython-313.pyc +0 -0
  120. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/batcher.cpython-313.pyc +0 -0
  121. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/dispatcher.cpython-313.pyc +0 -0
  122. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/generator.cpython-313.pyc +0 -0
  123. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/history.cpython-313.pyc +0 -0
  124. package/omega/Agentik_Engine/omega_engine/audits/__pycache__/pipeline.cpython-313.pyc +0 -0
  125. package/omega/Agentik_Engine/omega_engine/audits/batcher.py +218 -0
  126. package/omega/Agentik_Engine/omega_engine/audits/dispatcher.py +92 -0
  127. package/omega/Agentik_Engine/omega_engine/audits/generator.py +234 -0
  128. package/omega/Agentik_Engine/omega_engine/audits/history.py +168 -0
  129. package/omega/Agentik_Engine/omega_engine/audits/pipeline.py +198 -0
  130. package/omega/Agentik_Engine/omega_engine/auto_update.py +339 -0
  131. package/omega/Agentik_Engine/omega_engine/backup.py +215 -0
  132. package/omega/Agentik_Engine/omega_engine/cadence.py +158 -0
  133. package/omega/Agentik_Engine/omega_engine/classifier.py +215 -0
  134. package/omega/Agentik_Engine/omega_engine/cleanup.py +673 -0
  135. package/omega/Agentik_Engine/omega_engine/cli.py +4156 -86
  136. package/omega/Agentik_Engine/omega_engine/completions.py +260 -0
  137. package/omega/Agentik_Engine/omega_engine/costs.py +100 -0
  138. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/__init__.cpython-313.pyc +0 -0
  139. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/autonomous.cpython-313.pyc +0 -0
  140. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/engine.cpython-313.pyc +0 -0
  141. package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/telegram.cpython-313.pyc +0 -0
  142. package/omega/Agentik_Engine/omega_engine/daemons/engine.py +53 -4
  143. package/omega/Agentik_Engine/omega_engine/daemons/telegram.py +101 -17
  144. package/omega/Agentik_Engine/omega_engine/done_signal.py +154 -0
  145. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/__init__.cpython-313.pyc +0 -0
  146. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/artifact.cpython-313.pyc +0 -0
  147. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/automation.cpython-313.pyc +0 -0
  148. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/base.cpython-313.pyc +0 -0
  149. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/claudecode.cpython-313.pyc +0 -0
  150. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/connection.cpython-313.pyc +0 -0
  151. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/coworker.cpython-313.pyc +0 -0
  152. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/loop.cpython-313.pyc +0 -0
  153. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/prompt.cpython-313.pyc +0 -0
  154. package/omega/Agentik_Engine/omega_engine/educators/__pycache__/skill.cpython-313.pyc +0 -0
  155. package/omega/Agentik_Engine/omega_engine/envelope.py +219 -0
  156. package/omega/Agentik_Engine/omega_engine/executor.py +149 -10
  157. package/omega/Agentik_Engine/omega_engine/genesis/__init__.py +134 -0
  158. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/__init__.cpython-313.pyc +0 -0
  159. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/orchestrator.cpython-313.pyc +0 -0
  160. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/phases.cpython-313.pyc +0 -0
  161. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/stack.cpython-313.pyc +0 -0
  162. package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/state.cpython-313.pyc +0 -0
  163. package/omega/Agentik_Engine/omega_engine/genesis/orchestrator.py +262 -0
  164. package/omega/Agentik_Engine/omega_engine/genesis/phases.py +950 -0
  165. package/omega/Agentik_Engine/omega_engine/genesis/stack.py +324 -0
  166. package/omega/Agentik_Engine/omega_engine/genesis/state.py +353 -0
  167. package/omega/Agentik_Engine/omega_engine/handoff.py +459 -0
  168. package/omega/Agentik_Engine/omega_engine/hermes.py +426 -0
  169. package/omega/Agentik_Engine/omega_engine/hermes_bootstrap.py +382 -0
  170. package/omega/Agentik_Engine/omega_engine/hermes_desktop.py +469 -0
  171. package/omega/Agentik_Engine/omega_engine/integrations/__init__.py +30 -0
  172. package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/__init__.cpython-313.pyc +0 -0
  173. package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/graphify.cpython-313.pyc +0 -0
  174. package/omega/Agentik_Engine/omega_engine/integrations/graphify.py +234 -0
  175. package/omega/Agentik_Engine/omega_engine/learning.py +268 -0
  176. package/omega/Agentik_Engine/omega_engine/managed_agent.py +467 -0
  177. package/omega/Agentik_Engine/omega_engine/memory.py +271 -0
  178. package/omega/Agentik_Engine/omega_engine/menu.py +1065 -0
  179. package/omega/Agentik_Engine/omega_engine/migrations/__init__.py +144 -0
  180. package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
  181. package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/v0_14_0.cpython-313.pyc +0 -0
  182. package/omega/Agentik_Engine/omega_engine/migrations/v0_14_0.py +29 -0
  183. package/omega/Agentik_Engine/omega_engine/mission.py +16 -13
  184. package/omega/Agentik_Engine/omega_engine/plan.py +846 -0
  185. package/omega/Agentik_Engine/omega_engine/prompts.py +158 -0
  186. package/omega/Agentik_Engine/omega_engine/provider.py +161 -12
  187. package/omega/Agentik_Engine/omega_engine/prune.py +151 -0
  188. package/omega/Agentik_Engine/omega_engine/pursue.py +205 -0
  189. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/__init__.cpython-313.pyc +0 -0
  190. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/agentic.cpython-313.pyc +0 -0
  191. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/base.cpython-313.pyc +0 -0
  192. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/corrective.cpython-313.pyc +0 -0
  193. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/graph.cpython-313.pyc +0 -0
  194. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/hybrid.cpython-313.pyc +0 -0
  195. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/multimodal.cpython-313.pyc +0 -0
  196. package/omega/Agentik_Engine/omega_engine/rag/__pycache__/router.cpython-313.pyc +0 -0
  197. package/omega/Agentik_Engine/omega_engine/router.py +28 -0
  198. package/omega/Agentik_Engine/omega_engine/skill_discovery/__init__.py +48 -0
  199. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/__init__.cpython-313.pyc +0 -0
  200. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/auditor.cpython-313.pyc +0 -0
  201. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/finder.cpython-313.pyc +0 -0
  202. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/installer.cpython-313.pyc +0 -0
  203. package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/marketplaces.cpython-313.pyc +0 -0
  204. package/omega/Agentik_Engine/omega_engine/skill_discovery/auditor.py +232 -0
  205. package/omega/Agentik_Engine/omega_engine/skill_discovery/finder.py +94 -0
  206. package/omega/Agentik_Engine/omega_engine/skill_discovery/installer.py +129 -0
  207. package/omega/Agentik_Engine/omega_engine/skill_discovery/marketplaces.py +80 -0
  208. package/omega/Agentik_Engine/omega_engine/skill_routing.py +388 -0
  209. package/omega/Agentik_Engine/omega_engine/smoke.py +81 -0
  210. package/omega/Agentik_Engine/omega_engine/store.py +88 -41
  211. package/omega/Agentik_Engine/omega_engine/sync.py +142 -1
  212. package/omega/Agentik_Engine/omega_engine/telegram_history.py +260 -0
  213. package/omega/Agentik_Engine/omega_engine/tmux.py +526 -0
  214. package/omega/Agentik_Engine/omega_engine/understand_anything.py +275 -0
  215. package/omega/Agentik_Engine/omega_engine/updater.py +70 -0
  216. package/omega/Agentik_Engine/omega_engine/validate.py +186 -0
  217. package/omega/Agentik_Engine/omega_engine/vault.py +342 -0
  218. package/omega/Agentik_Engine/omega_engine/webhooks.py +262 -0
  219. package/omega/Agentik_Engine/omega_engine/worker.py +526 -0
  220. package/omega/Agentik_Engine/pyproject.toml +1 -1
  221. package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313-pytest-8.4.2.pyc +0 -0
  222. package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313.pyc +0 -0
  223. package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313-pytest-8.4.2.pyc +0 -0
  224. package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313.pyc +0 -0
  225. package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313-pytest-8.4.2.pyc +0 -0
  226. package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313.pyc +0 -0
  227. package/omega/Agentik_Engine/tests/__pycache__/test_audit_arsenal.cpython-313-pytest-8.4.2.pyc +0 -0
  228. package/omega/Agentik_Engine/tests/__pycache__/test_audit_arsenal.cpython-313.pyc +0 -0
  229. package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313-pytest-8.4.2.pyc +0 -0
  230. package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313.pyc +0 -0
  231. package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313-pytest-8.4.2.pyc +0 -0
  232. package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313.pyc +0 -0
  233. package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313-pytest-8.4.2.pyc +0 -0
  234. package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313.pyc +0 -0
  235. package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313-pytest-8.4.2.pyc +0 -0
  236. package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313.pyc +0 -0
  237. package/omega/Agentik_Engine/tests/__pycache__/test_executor.cpython-313-pytest-8.4.2.pyc +0 -0
  238. package/omega/Agentik_Engine/tests/__pycache__/test_executor.cpython-313.pyc +0 -0
  239. package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313-pytest-8.4.2.pyc +0 -0
  240. package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313.pyc +0 -0
  241. package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313-pytest-8.4.2.pyc +0 -0
  242. package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313.pyc +0 -0
  243. package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313-pytest-8.4.2.pyc +0 -0
  244. package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313.pyc +0 -0
  245. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313-pytest-8.4.2.pyc +0 -0
  246. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313.pyc +0 -0
  247. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313-pytest-8.4.2.pyc +0 -0
  248. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313.pyc +0 -0
  249. package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313-pytest-8.4.2.pyc +0 -0
  250. package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313.pyc +0 -0
  251. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313-pytest-8.4.2.pyc +0 -0
  252. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313.pyc +0 -0
  253. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
  254. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
  255. package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313-pytest-8.4.2.pyc +0 -0
  256. package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313.pyc +0 -0
  257. package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313-pytest-8.4.2.pyc +0 -0
  258. package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313.pyc +0 -0
  259. package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313-pytest-8.4.2.pyc +0 -0
  260. package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313.pyc +0 -0
  261. package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313-pytest-8.4.2.pyc +0 -0
  262. package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313.pyc +0 -0
  263. package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313-pytest-8.4.2.pyc +0 -0
  264. package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313.pyc +0 -0
  265. package/omega/Agentik_Engine/tests/__pycache__/test_mission.cpython-313-pytest-8.4.2.pyc +0 -0
  266. package/omega/Agentik_Engine/tests/__pycache__/test_mission.cpython-313.pyc +0 -0
  267. package/omega/Agentik_Engine/tests/__pycache__/test_progress.cpython-313-pytest-8.4.2.pyc +0 -0
  268. package/omega/Agentik_Engine/tests/__pycache__/test_progress.cpython-313.pyc +0 -0
  269. package/omega/Agentik_Engine/tests/__pycache__/test_project.cpython-313-pytest-8.4.2.pyc +0 -0
  270. package/omega/Agentik_Engine/tests/__pycache__/test_project.cpython-313.pyc +0 -0
  271. package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313-pytest-8.4.2.pyc +0 -0
  272. package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313.pyc +0 -0
  273. package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313-pytest-8.4.2.pyc +0 -0
  274. package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313.pyc +0 -0
  275. package/omega/Agentik_Engine/tests/__pycache__/test_reducer.cpython-313-pytest-8.4.2.pyc +0 -0
  276. package/omega/Agentik_Engine/tests/__pycache__/test_reducer.cpython-313.pyc +0 -0
  277. package/omega/Agentik_Engine/tests/__pycache__/test_report.cpython-313-pytest-8.4.2.pyc +0 -0
  278. package/omega/Agentik_Engine/tests/__pycache__/test_report.cpython-313.pyc +0 -0
  279. package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313-pytest-8.4.2.pyc +0 -0
  280. package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313.pyc +0 -0
  281. package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313-pytest-8.4.2.pyc +0 -0
  282. package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313.pyc +0 -0
  283. package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313-pytest-8.4.2.pyc +0 -0
  284. package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313.pyc +0 -0
  285. package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313-pytest-8.4.2.pyc +0 -0
  286. package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313.pyc +0 -0
  287. package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313-pytest-8.4.2.pyc +0 -0
  288. package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313.pyc +0 -0
  289. package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313-pytest-8.4.2.pyc +0 -0
  290. package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313.pyc +0 -0
  291. package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc +0 -0
  292. package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313.pyc +0 -0
  293. package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313-pytest-8.4.2.pyc +0 -0
  294. package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313.pyc +0 -0
  295. package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313-pytest-8.4.2.pyc +0 -0
  296. package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313.pyc +0 -0
  297. package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313-pytest-8.4.2.pyc +0 -0
  298. package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313.pyc +0 -0
  299. package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313-pytest-8.4.2.pyc +0 -0
  300. package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313.pyc +0 -0
  301. package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313-pytest-8.4.2.pyc +0 -0
  302. package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313.pyc +0 -0
  303. package/omega/Agentik_Engine/tests/test_account.py +8 -3
  304. package/omega/Agentik_Engine/tests/test_adversarial.py +351 -0
  305. package/omega/Agentik_Engine/tests/test_agents_envelope.py +274 -0
  306. package/omega/Agentik_Engine/tests/test_audits_pipeline.py +348 -0
  307. package/omega/Agentik_Engine/tests/test_auto_update_and_migrations.py +394 -0
  308. package/omega/Agentik_Engine/tests/test_genesis_and_plan.py +573 -0
  309. package/omega/Agentik_Engine/tests/test_graphify.py +190 -0
  310. package/omega/Agentik_Engine/tests/test_handoff.py +311 -0
  311. package/omega/Agentik_Engine/tests/test_hermes_and_ua.py +387 -0
  312. package/omega/Agentik_Engine/tests/test_hermes_bootstrap_and_desktop.py +358 -0
  313. package/omega/Agentik_Engine/tests/test_install_steps.py +359 -0
  314. package/omega/Agentik_Engine/tests/test_install_ux.py +151 -0
  315. package/omega/Agentik_Engine/tests/test_installer_wiring.py +496 -0
  316. package/omega/Agentik_Engine/tests/test_intelligence.py +285 -0
  317. package/omega/Agentik_Engine/tests/test_llm_clis_and_uninstall.py +228 -0
  318. package/omega/Agentik_Engine/tests/test_managed_agent.py +363 -0
  319. package/omega/Agentik_Engine/tests/test_max_provider_and_menu.py +231 -0
  320. package/omega/Agentik_Engine/tests/test_menu_coverage.py +72 -0
  321. package/omega/Agentik_Engine/tests/test_pursue_cadence.py +217 -0
  322. package/omega/Agentik_Engine/tests/test_role_aliases_and_ssot.py +207 -0
  323. package/omega/Agentik_Engine/tests/test_skill_discovery_and_gate.py +337 -0
  324. package/omega/Agentik_Engine/tests/test_skill_power.py +259 -0
  325. package/omega/Agentik_Engine/tests/test_skill_routing.py +189 -0
  326. package/omega/Agentik_Engine/tests/test_telegram_history.py +209 -0
  327. package/omega/Agentik_Engine/tests/test_tmux_and_aisb_chat.py +223 -0
  328. package/omega/Agentik_Engine/tests/test_v06_features.py +370 -0
  329. package/omega/Agentik_Engine/tests/test_vault.py +173 -0
  330. package/omega/Agentik_Engine/tests/test_webhooks_and_readiness.py +277 -0
  331. package/omega/Agentik_Engine/tests/test_worker_and_cleanup.py +541 -0
  332. package/omega/Agentik_Extra/etc/secrets/.vault-key +3 -0
  333. package/omega/Agentik_Extra/etc/secrets/.vault-pub +1 -0
  334. package/omega/Agentik_Runtime/audits.db +0 -0
  335. package/omega/Agentik_SSOT/VERSION +1 -1
  336. package/omega/Agentik_SSOT/claude-plugins/claude-plugins.yaml +100 -0
  337. package/omega/Agentik_SSOT/docs/LAYERS.md +90 -0
  338. package/omega/Agentik_SSOT/docs/USER-JOURNEY.md +283 -0
  339. package/omega/Agentik_SSOT/docs/quality-arsenal/ARSENAL-INTERCONNECTIONS.md +283 -0
  340. package/omega/Agentik_SSOT/docs/quality-arsenal/ARSENAL-ORCHESTRATION-PLAYBOOK.md +364 -0
  341. package/omega/Agentik_SSOT/docs/quality-arsenal/AUDIT-VERIFICATION-CONTRACT.md +272 -0
  342. package/omega/Agentik_SSOT/docs/quality-arsenal/QUALITY-ARSENAL-PREAMBLE.md +462 -0
  343. package/omega/Agentik_SSOT/marketplaces/design-discipline.yaml +86 -0
  344. package/omega/Agentik_SSOT/skills/a11yaudit/SKILL.md +161 -0
  345. package/omega/Agentik_SSOT/skills/apiaudit/SKILL.md +157 -0
  346. package/omega/Agentik_SSOT/skills/audit-orchestrator.md +212 -0
  347. package/omega/Agentik_SSOT/skills/audit-pilot.md +466 -0
  348. package/omega/Agentik_SSOT/skills/audit-tracker.md +147 -0
  349. package/omega/Agentik_SSOT/skills/automationaudit/SKILL.md +161 -0
  350. package/omega/Agentik_SSOT/skills/cadence/SKILL.md +76 -0
  351. package/omega/Agentik_SSOT/skills/codeaudit/SKILL.md +153 -0
  352. package/omega/Agentik_SSOT/skills/copyaudit/SKILL.md +161 -0
  353. package/omega/Agentik_SSOT/skills/dataaudit/SKILL.md +157 -0
  354. package/omega/Agentik_SSOT/skills/debugaudit/SKILL.md +161 -0
  355. package/omega/Agentik_SSOT/skills/dispatch/SKILL.md +79 -0
  356. package/omega/Agentik_SSOT/skills/dxaudit/SKILL.md +161 -0
  357. package/omega/Agentik_SSOT/skills/featureaudit/SKILL.md +161 -0
  358. package/omega/Agentik_SSOT/skills/flowaudit/SKILL.md +165 -0
  359. package/omega/Agentik_SSOT/skills/genesis/SKILL.md +116 -0
  360. package/omega/Agentik_SSOT/skills/handoff/SKILL.md +117 -0
  361. package/omega/Agentik_SSOT/skills/logicaudit/SKILL.md +165 -0
  362. package/omega/Agentik_SSOT/skills/motionaudit/SKILL.md +165 -0
  363. package/omega/Agentik_SSOT/skills/newcmd.md +300 -0
  364. package/omega/Agentik_SSOT/skills/perfaudit/SKILL.md +161 -0
  365. package/omega/Agentik_SSOT/skills/plan/SKILL.md +127 -0
  366. package/omega/Agentik_SSOT/skills/pursue/SKILL.md +68 -0
  367. package/omega/Agentik_SSOT/skills/quality-arsenal.md +180 -0
  368. package/omega/Agentik_SSOT/skills/rag-route.md +9 -0
  369. package/omega/Agentik_SSOT/skills/refontaudit/SKILL.md +165 -0
  370. package/omega/Agentik_SSOT/skills/retentionaudit/SKILL.md +165 -0
  371. package/omega/Agentik_SSOT/skills/secaudit/SKILL.md +157 -0
  372. package/omega/Agentik_SSOT/skills/seoaudit/SKILL.md +161 -0
  373. package/omega/Agentik_SSOT/skills/skill-auditor/SKILL.md +83 -0
  374. package/omega/Agentik_SSOT/skills/skill-finder/SKILL.md +116 -0
  375. package/omega/Agentik_SSOT/skills/uiuxaudit/SKILL.md +165 -0
  376. package/package.json +2 -2
@@ -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
@@ -0,0 +1,262 @@
1
+ """Inbound webhooks — GitHub / Linear / generic HMAC-SHA256.
2
+
3
+ The autonomous supervisor already has ``wake_webhook(path, payload)``. The
4
+ gap was the *HTTP front door*: nothing was listening on a port, nothing
5
+ verified signatures. This module is that front door.
6
+
7
+ Specs live in ``Agentik_SSOT/webhooks/webhooks.yaml`` (one path = one
8
+ spec). The engine daemon's HTTP server handles ``POST /webhook/<id>``:
9
+ it locates the spec, reads the source-specific signature header, looks
10
+ up the HMAC secret in the vault, verifies, and calls
11
+ ``supervisor.wake_webhook(path, payload)``. On a valid signature the
12
+ caller gets ``202 Accepted`` immediately — the mission runs async.
13
+
14
+ We never accept an unsigned webhook. The default is ``source: generic``
15
+ which still requires HMAC-SHA256 in a configurable header.
16
+
17
+ Why we ship this small (no fancy framework):
18
+
19
+ * The engine daemon is already on a loopback port. The operator's
20
+ reverse proxy (nginx, Cloudflare Tunnel) is responsible for the
21
+ public face. We do auth, not networking.
22
+ * Signature verification is the only thing that matters — without it,
23
+ a webhook endpoint is a remote code execution waiting to happen.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ import hmac
29
+ import json
30
+ import os
31
+ from dataclasses import dataclass, field
32
+ from pathlib import Path
33
+ from typing import Any
34
+
35
+ import yaml
36
+
37
+
38
+ # Source-specific header names. The signature value's wire format is
39
+ # different per source; see _verify_source for the parsing.
40
+ _SIGNATURE_HEADER = {
41
+ "github": "X-Hub-Signature-256",
42
+ "linear": "Linear-Signature",
43
+ "generic": "X-Omega-Signature",
44
+ }
45
+
46
+
47
+ @dataclass
48
+ class WebhookSpec:
49
+ """One inbound webhook endpoint."""
50
+ id: str
51
+ path: str # "/webhook/github-prs"
52
+ source: str = "generic" # "github" | "linear" | "generic"
53
+ secret_ref: str = "" # vault ref for the HMAC secret
54
+ description: str = ""
55
+
56
+ @staticmethod
57
+ def from_dict(d: dict[str, Any]) -> "WebhookSpec":
58
+ return WebhookSpec(
59
+ id=str(d["id"]),
60
+ path=str(d.get("path") or f"/webhook/{d['id']}"),
61
+ source=str(d.get("source", "generic")),
62
+ secret_ref=str(d.get("secret_ref",
63
+ f"WEBHOOK_{d['id'].upper().replace('-', '_')}")),
64
+ description=str(d.get("description", "")),
65
+ )
66
+
67
+
68
+ @dataclass
69
+ class WebhookRegistry:
70
+ """All webhook specs known to the engine."""
71
+ specs: list[WebhookSpec] = field(default_factory=list)
72
+
73
+ def by_path(self, path: str) -> WebhookSpec | None:
74
+ for s in self.specs:
75
+ if s.path == path:
76
+ return s
77
+ return None
78
+
79
+ def by_id(self, id: str) -> WebhookSpec | None:
80
+ for s in self.specs:
81
+ if s.id == id:
82
+ return s
83
+ return None
84
+
85
+
86
+ def load_registry(omega_home: str | Path) -> WebhookRegistry:
87
+ """Read ``Agentik_SSOT/webhooks/webhooks.yaml``. Empty if missing."""
88
+ path = (Path(omega_home) / "Agentik_SSOT" / "webhooks" / "webhooks.yaml")
89
+ if not path.exists():
90
+ return WebhookRegistry()
91
+ data = yaml.safe_load(path.read_text()) or {}
92
+ specs = [
93
+ WebhookSpec.from_dict(d)
94
+ for d in (data.get("webhooks") or [])
95
+ if isinstance(d, dict) and d.get("id")
96
+ ]
97
+ return WebhookRegistry(specs=specs)
98
+
99
+
100
+ def signature_header_for(source: str) -> str:
101
+ """Return the header name the signature is expected in."""
102
+ return _SIGNATURE_HEADER.get(source.lower(), _SIGNATURE_HEADER["generic"])
103
+
104
+
105
+ def verify_signature(
106
+ source: str,
107
+ body: bytes,
108
+ signature_header_value: str | None,
109
+ secret: str,
110
+ *,
111
+ timestamp_header_value: str | None = None,
112
+ replay_window_s: int = 300,
113
+ now_s: float | None = None,
114
+ ) -> tuple[bool, str]:
115
+ """Return (ok, reason) for a webhook signature.
116
+
117
+ Hard rule: missing signature → False. Missing secret → False. We
118
+ NEVER let an unsigned webhook through.
119
+
120
+ Replay protection
121
+ -----------------
122
+ When ``timestamp_header_value`` is provided, we require the timestamp
123
+ to be within ``replay_window_s`` seconds of ``now_s``. An attacker who
124
+ captured a valid signed body can otherwise replay it forever. Callers
125
+ SHOULD pass the timestamp header (X-Omega-Timestamp / X-Hub-Timestamp /
126
+ Linear-Delivery-Timestamp); when absent we accept the request (HMAC
127
+ alone is the v0.5 contract; timestamp is additive).
128
+
129
+ Source-specific notes:
130
+
131
+ * ``github`` ships ``X-Hub-Signature-256: sha256=<hex>``.
132
+ * ``linear`` ships ``Linear-Signature: <hex>`` (no prefix).
133
+ * ``generic`` expects ``X-Omega-Signature: sha256=<hex>``.
134
+ """
135
+ if not signature_header_value:
136
+ return False, "missing signature header"
137
+ if not secret:
138
+ return False, "secret not set in vault"
139
+
140
+ # Replay window check (additive, off when no timestamp supplied).
141
+ if timestamp_header_value is not None:
142
+ try:
143
+ ts = float(timestamp_header_value)
144
+ except (TypeError, ValueError):
145
+ return False, "timestamp header is not numeric"
146
+ import time as _t
147
+ wall = now_s if now_s is not None else _t.time()
148
+ if abs(wall - ts) > replay_window_s:
149
+ return False, (
150
+ f"timestamp outside ±{replay_window_s}s replay window "
151
+ f"(now={wall:.0f} hdr={ts:.0f})"
152
+ )
153
+
154
+ src = source.lower()
155
+ raw_sig = signature_header_value.strip()
156
+ if src in ("github", "generic"):
157
+ # `sha256=<hex>` form
158
+ if not raw_sig.startswith("sha256="):
159
+ return False, "signature missing sha256= prefix"
160
+ provided = raw_sig.split("=", 1)[1]
161
+ elif src == "linear":
162
+ # raw hex
163
+ provided = raw_sig
164
+ else:
165
+ return False, f"unknown source {source!r}"
166
+
167
+ mac = hmac.new(
168
+ secret.encode("utf-8"), body, hashlib.sha256,
169
+ ).hexdigest()
170
+ if not hmac.compare_digest(mac, provided):
171
+ return False, "signature mismatch"
172
+ return True, "ok"
173
+
174
+
175
+ def _load_secret(omega_home: str | Path, secret_ref: str) -> str:
176
+ """Best-effort vault read."""
177
+ if not secret_ref:
178
+ return ""
179
+ try:
180
+ from omega_engine.vault import vault_read
181
+ v = vault_read(omega_home, secret_ref)
182
+ return v or ""
183
+ except Exception: # noqa: BLE001
184
+ return ""
185
+
186
+
187
+ def handle_webhook(
188
+ omega_home: str | Path,
189
+ *,
190
+ path: str,
191
+ headers: dict[str, str],
192
+ body: bytes,
193
+ supervisor=None,
194
+ ) -> dict[str, Any]:
195
+ """Run the gate for one inbound webhook.
196
+
197
+ Returns ``{status: int, ok: bool, reason: str, fired: [charter_ids]}``.
198
+ Status codes: 200 (already-known but no charter fired), 202 (charters
199
+ fired), 401 (bad signature), 404 (unknown path), 500 (internal).
200
+
201
+ ``supervisor`` is optional — pass an ``AutonomousSupervisor`` to
202
+ actually fire charters. Without one, we still verify but don't fire
203
+ (useful for ``omega webhook test``).
204
+ """
205
+ home = Path(omega_home)
206
+ registry = load_registry(home)
207
+ spec = registry.by_path(path)
208
+ if spec is None:
209
+ return {"status": 404, "ok": False, "reason": "no spec for path",
210
+ "fired": []}
211
+
212
+ secret = _load_secret(home, spec.secret_ref)
213
+ sig_header_name = signature_header_for(spec.source)
214
+ sig_value = None
215
+ # Headers come from a stdlib BaseHTTPRequestHandler which lower-cases
216
+ # access via dict-style only when wrapped; here we normalise both sides.
217
+ for k, v in headers.items():
218
+ if k.lower() == sig_header_name.lower():
219
+ sig_value = v
220
+ break
221
+ ok, reason = verify_signature(spec.source, body, sig_value, secret)
222
+ if not ok:
223
+ return {"status": 401, "ok": False, "reason": reason, "fired": []}
224
+
225
+ # Parse payload best-effort; webhooks SHOULD be JSON.
226
+ try:
227
+ payload = json.loads(body.decode("utf-8") or "{}")
228
+ except (json.JSONDecodeError, UnicodeDecodeError):
229
+ payload = {"_raw": body.decode("utf-8", errors="replace")[:1000]}
230
+
231
+ fired: list[str] = []
232
+ if supervisor is not None:
233
+ try:
234
+ fired = supervisor.wake_webhook(path, payload) or []
235
+ except Exception as exc: # noqa: BLE001
236
+ return {
237
+ "status": 500, "ok": False,
238
+ "reason": f"supervisor raised: {exc}"[:300],
239
+ "fired": [],
240
+ }
241
+
242
+ return {
243
+ "status": 202 if fired else 200,
244
+ "ok": True,
245
+ "reason": "ok",
246
+ "fired": fired,
247
+ }
248
+
249
+
250
+ def generate_test_signature(source: str, body: bytes, secret: str) -> str:
251
+ """Build a valid signature header value for the given source.
252
+
253
+ Used by ``omega webhook test`` and by the integration tests so we can
254
+ POST a synthetic webhook to the engine and confirm the gate works.
255
+ """
256
+ mac = hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest()
257
+ src = source.lower()
258
+ if src in ("github", "generic"):
259
+ return f"sha256={mac}"
260
+ if src == "linear":
261
+ return mac
262
+ raise ValueError(f"unknown source: {source}")