@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,502 @@
1
+ """The Claude Code Max account pool + per-account billing.
2
+
3
+ > Omega OS runs ONE engine process, not N tmux sessions. There is nothing to
4
+ > "switch" globally. The Claude provider holds a POOL and assigns each agent
5
+ > call to an account, so N Max accounts give the SUM of their rate limits as
6
+ > usable throughput. See `docs/ACCOUNT-AND-BILLING.md` for the design.
7
+
8
+ This module is the real implementation of that pool — plus the OAuth login
9
+ flow, the encrypted-vault file format, and the billing aggregator that scans
10
+ the event log for per-account token usage.
11
+
12
+ Stdlib only (yaml is already a top-level dep of omega-engine).
13
+
14
+ ──── ON THE CLAUDE OAUTH DEVICE-CODE FLOW ────
15
+
16
+ Anthropic does **not** publicly document an RFC 8628 device-authorization-grant
17
+ endpoint for Claude Max accounts. The official Claude Code Max OAuth flow is
18
+ browser-based PKCE through `https://claude.ai`, which a headless VPS cannot
19
+ complete without launching a browser.
20
+
21
+ `claude_device_code_flow` therefore implements RFC 8628 correctly against
22
+ *configurable* endpoints (the spec is the spec — when Anthropic publishes the
23
+ URLs, the code already works). If the endpoint is unreachable or returns a
24
+ non-device-code response, the function raises with a clear message and the CLI
25
+ falls back to a **manual paste flow**: the user logs in via browser on another
26
+ machine, copies the OAuth token, pastes it into the prompt, and we write it
27
+ into the vault with `chmod 600`. That manual fallback is the path that always
28
+ works today — not a stub, a working credentials-into-the-vault flow.
29
+ """
30
+ from __future__ import annotations
31
+
32
+ import json
33
+ import os
34
+ import stat
35
+ import time
36
+ import urllib.error
37
+ import urllib.parse
38
+ import urllib.request
39
+ from dataclasses import dataclass, field, asdict
40
+ from pathlib import Path
41
+ from typing import Any
42
+
43
+ # yaml is a hard dependency of omega-engine — see pyproject.toml.
44
+ import yaml
45
+
46
+
47
+ __all__ = [
48
+ "ClaudeAccount",
49
+ "AccountPool",
50
+ "BillingAggregator",
51
+ "vault_path",
52
+ "read_token",
53
+ "write_token",
54
+ "claude_oauth_login_url",
55
+ "claude_device_code_flow",
56
+ "ACCOUNT_DEVICE_AUTH_URL",
57
+ "ACCOUNT_DEVICE_TOKEN_URL",
58
+ ]
59
+
60
+
61
+ # ──── OAuth endpoint constants ────
62
+ # Configurable via env so tests / future Anthropic endpoint changes do not
63
+ # require a code edit. Defaults reflect the documented RFC 8628 shape; if
64
+ # Anthropic publishes the actual URLs, set them with these env vars (or update
65
+ # the constants here in one place).
66
+ ACCOUNT_DEVICE_AUTH_URL = os.environ.get(
67
+ "OMEGA_CLAUDE_DEVICE_AUTH_URL",
68
+ "https://auth.anthropic.com/oauth/device/authorize",
69
+ )
70
+ ACCOUNT_DEVICE_TOKEN_URL = os.environ.get(
71
+ "OMEGA_CLAUDE_DEVICE_TOKEN_URL",
72
+ "https://auth.anthropic.com/oauth/device/token",
73
+ )
74
+ ACCOUNT_BROWSER_LOGIN_URL = "https://claude.ai/oauth"
75
+
76
+
77
+ # ──── data model ────
78
+
79
+
80
+ @dataclass
81
+ class ClaudeAccount:
82
+ """One Claude Code Max account entry in the pool.
83
+
84
+ `secret_ref` is the only handle to the OAuth token — the token itself
85
+ lives in `Agentik_Extra/etc/secrets/<secret_ref>.env` with mode 600.
86
+ """
87
+
88
+ id: str
89
+ label: str = ""
90
+ secret_ref: str = ""
91
+ weight: int = 1
92
+ status: str = "active" # active | resting | disabled
93
+ last_used_at: float = 0.0
94
+ tokens_used: int = 0
95
+
96
+ def to_yaml(self) -> dict[str, Any]:
97
+ """Serialize for accounts.yaml. Skips zero-valued runtime fields so the
98
+ on-disk file stays clean for accounts that have never been used."""
99
+ out: dict[str, Any] = {
100
+ "id": self.id,
101
+ "label": self.label,
102
+ "secret_ref": self.secret_ref,
103
+ "weight": int(self.weight),
104
+ "status": self.status,
105
+ }
106
+ if self.last_used_at:
107
+ out["last_used_at"] = float(self.last_used_at)
108
+ if self.tokens_used:
109
+ out["tokens_used"] = int(self.tokens_used)
110
+ return out
111
+
112
+ @classmethod
113
+ def from_yaml(cls, row: dict[str, Any]) -> "ClaudeAccount":
114
+ return cls(
115
+ id=str(row.get("id", "")).strip(),
116
+ label=str(row.get("label", "")),
117
+ secret_ref=str(row.get("secret_ref", "")),
118
+ weight=int(row.get("weight", 1)),
119
+ status=str(row.get("status", "active")),
120
+ last_used_at=float(row.get("last_used_at", 0.0) or 0.0),
121
+ tokens_used=int(row.get("tokens_used", 0) or 0),
122
+ )
123
+
124
+
125
+ @dataclass
126
+ class AccountPool:
127
+ """The pool of Claude Code Max accounts.
128
+
129
+ `selection` controls how `next()` picks the account for the next call:
130
+ - `round-robin` — strict rotation among active accounts
131
+ - `least-used` — pick the active account with the smallest `tokens_used`
132
+ - `by-quota` — like least-used but weighted by `weight`
133
+ """
134
+
135
+ accounts: list[ClaudeAccount] = field(default_factory=list)
136
+ selection: str = "least-used"
137
+ version: int = 1
138
+ _rr_cursor: int = 0 # private; used by round-robin
139
+
140
+ # ──── loading & saving ────────────────────────────────────────────────
141
+
142
+ @classmethod
143
+ def load(cls, omega_home: str | Path) -> "AccountPool":
144
+ """Load the pool. Prefers `accounts.yaml`, falls back to
145
+ `accounts.example.yaml`. Returns an empty pool if neither exists."""
146
+ home = Path(omega_home)
147
+ cfg_dir = home / "Agentik_Providers" / "claude"
148
+ for name in ("accounts.yaml", "accounts.example.yaml"):
149
+ cfg = cfg_dir / name
150
+ if cfg.exists():
151
+ return cls._from_file(cfg)
152
+ return cls()
153
+
154
+ @classmethod
155
+ def _from_file(cls, path: Path) -> "AccountPool":
156
+ raw = yaml.safe_load(path.read_text()) or {}
157
+ accounts = [ClaudeAccount.from_yaml(r) for r in (raw.get("pool") or [])]
158
+ return cls(
159
+ accounts=accounts,
160
+ selection=str(raw.get("selection", "least-used")),
161
+ version=int(raw.get("version", 1) or 1),
162
+ )
163
+
164
+ def save(self, omega_home: str | Path) -> Path:
165
+ """Persist the pool to `accounts.yaml` (creates parent dirs).
166
+
167
+ Always writes to the canonical name (`accounts.yaml`), never to the
168
+ example file — that one is the template that ships with the repo.
169
+ """
170
+ home = Path(omega_home)
171
+ cfg_dir = home / "Agentik_Providers" / "claude"
172
+ cfg_dir.mkdir(parents=True, exist_ok=True)
173
+ cfg = cfg_dir / "accounts.yaml"
174
+ data = {
175
+ "version": self.version,
176
+ "selection": self.selection,
177
+ "pool": [a.to_yaml() for a in self.accounts],
178
+ }
179
+ # default_flow_style=False keeps it human-readable; sort_keys=False
180
+ # preserves the natural field order (id, label, secret_ref, ...).
181
+ cfg.write_text(yaml.safe_dump(data, sort_keys=False, default_flow_style=False))
182
+ return cfg
183
+
184
+ # ──── selection ───────────────────────────────────────────────────────
185
+
186
+ def _active(self) -> list[ClaudeAccount]:
187
+ return [a for a in self.accounts if a.status == "active"]
188
+
189
+ def next(self) -> ClaudeAccount:
190
+ """Pick the next account per the current `selection` strategy.
191
+
192
+ Raises RuntimeError if no account is active — that's a real error the
193
+ caller (the Claude provider) must surface, not silently mock out.
194
+ """
195
+ active = self._active()
196
+ if not active:
197
+ raise RuntimeError(
198
+ "no active Claude Max accounts in the pool — "
199
+ "run `omega account login` or `omega account use <id> active`"
200
+ )
201
+ if self.selection == "round-robin":
202
+ chosen = active[self._rr_cursor % len(active)]
203
+ self._rr_cursor = (self._rr_cursor + 1) % len(active)
204
+ elif self.selection == "by-quota":
205
+ # weighted least-used: score = tokens_used / max(weight, 1).
206
+ # smallest score wins. ties broken by last_used_at (older first).
207
+ chosen = min(
208
+ active,
209
+ key=lambda a: (a.tokens_used / max(a.weight, 1), a.last_used_at),
210
+ )
211
+ else:
212
+ # least-used (default): smallest tokens_used, then oldest last_used.
213
+ chosen = min(active, key=lambda a: (a.tokens_used, a.last_used_at))
214
+ chosen.last_used_at = time.time()
215
+ return chosen
216
+
217
+ # ──── mutation helpers ────────────────────────────────────────────────
218
+
219
+ def add(self, account: ClaudeAccount) -> None:
220
+ """Add or replace (by id) an account in the pool."""
221
+ if not account.id:
222
+ raise ValueError("account.id must be set")
223
+ existing = self.get(account.id)
224
+ if existing is None:
225
+ self.accounts.append(account)
226
+ else:
227
+ # replace in place so list ordering is preserved
228
+ idx = self.accounts.index(existing)
229
+ self.accounts[idx] = account
230
+
231
+ def get(self, account_id: str) -> ClaudeAccount | None:
232
+ for a in self.accounts:
233
+ if a.id == account_id:
234
+ return a
235
+ return None
236
+
237
+ def set_status(self, account_id: str, status: str) -> None:
238
+ if status not in ("active", "resting", "disabled"):
239
+ raise ValueError(
240
+ f"invalid status '{status}' — must be active|resting|disabled"
241
+ )
242
+ acc = self.get(account_id)
243
+ if acc is None:
244
+ raise KeyError(f"no account '{account_id}' in the pool")
245
+ acc.status = status
246
+
247
+ def usage_for(self, account_id: str, tokens: int) -> None:
248
+ """Record `tokens` usage on `account_id`. Idempotent at the pool level
249
+ (the event log is the source of truth — this just updates the cached
250
+ counter shown by `omega account list`)."""
251
+ acc = self.get(account_id)
252
+ if acc is None:
253
+ return
254
+ acc.tokens_used += max(int(tokens), 0)
255
+ acc.last_used_at = time.time()
256
+
257
+
258
+ # ──── vault: where the OAuth tokens actually live ────────────────────────
259
+
260
+
261
+ def vault_path(omega_home: str | Path, secret_ref: str) -> Path:
262
+ """Return the on-disk path for a secret reference.
263
+
264
+ Convention: `Agentik_Extra/etc/secrets/<secret_ref>.env`. The directory is
265
+ created on demand with mode 700; individual secret files are mode 600.
266
+
267
+ Refuses path-traversal — `secret_ref` cannot contain `/` or `..`.
268
+ """
269
+ if not secret_ref:
270
+ raise ValueError("secret_ref must not be empty")
271
+ if "/" in secret_ref or "\\" in secret_ref or ".." in secret_ref:
272
+ raise ValueError(f"secret_ref must be a flat name, got {secret_ref!r}")
273
+ home = Path(omega_home)
274
+ secrets_dir = home / "Agentik_Extra" / "etc" / "secrets"
275
+ secrets_dir.mkdir(parents=True, exist_ok=True)
276
+ try:
277
+ os.chmod(secrets_dir, 0o700)
278
+ except (OSError, PermissionError):
279
+ # best-effort — file mode is enforced below where it matters most
280
+ pass
281
+ return secrets_dir / f"{secret_ref}.env"
282
+
283
+
284
+ def read_token(omega_home: str | Path, secret_ref: str) -> str:
285
+ """Read the OAuth token from the vault.
286
+
287
+ Resolution order, first match wins:
288
+ 1. ``vault_read(<secret_ref>_TOKEN)`` — the canonical key the new
289
+ ``write_token`` uses. If age is the active backend the read is
290
+ transparently decrypted.
291
+ 2. Legacy dotenv file at ``Agentik_Extra/etc/secrets/<secret_ref>.env``
292
+ with a ``CLAUDE_OAUTH_TOKEN=...`` line. Kept for backward compat
293
+ with installs that pre-date the encrypted vault.
294
+ """
295
+ # Local import to avoid an import cycle at module load time.
296
+ from omega_engine.vault import vault_read
297
+
298
+ canon = f"{secret_ref}_TOKEN"
299
+ try:
300
+ token = vault_read(omega_home, canon)
301
+ except ValueError:
302
+ token = None
303
+ if token:
304
+ return token.strip()
305
+
306
+ path = vault_path(omega_home, secret_ref)
307
+ if not path.exists():
308
+ raise FileNotFoundError(f"vault entry not found for ref {secret_ref!r}")
309
+ keys = ("CLAUDE_OAUTH_TOKEN", secret_ref.upper())
310
+ for line in path.read_text().splitlines():
311
+ s = line.strip()
312
+ if not s or s.startswith("#") or "=" not in s:
313
+ continue
314
+ k, _, v = s.partition("=")
315
+ k = k.strip()
316
+ v = v.strip().strip('"').strip("'")
317
+ if k in keys and v:
318
+ return v
319
+ raise ValueError(
320
+ f"no CLAUDE_OAUTH_TOKEN in vault file {path} — "
321
+ "expected a line like `CLAUDE_OAUTH_TOKEN=...`"
322
+ )
323
+
324
+
325
+ def write_token(omega_home: str | Path, secret_ref: str, token: str) -> Path:
326
+ """Write the OAuth token through the vault layer.
327
+
328
+ Uses ``omega_engine.vault.vault_write``: age-encrypted if the system has
329
+ ``age`` + ``age-keygen``, else mode-600 plaintext. Either way, the file
330
+ is owner-only on disk. Overwrites silently — rotating a token is the
331
+ expected use case. Returns the actual on-disk path written.
332
+ """
333
+ if not token or not token.strip():
334
+ raise ValueError("token must be a non-empty string")
335
+ from omega_engine.vault import vault_write
336
+ return vault_write(omega_home, f"{secret_ref}_TOKEN", token.strip())
337
+ return path
338
+
339
+
340
+ # ──── OAuth login flow ───────────────────────────────────────────────────
341
+
342
+
343
+ def claude_oauth_login_url() -> str:
344
+ """The URL a human visits to obtain a Claude OAuth token.
345
+
346
+ Returned as a plain string so the CLI can print it and instruct the user.
347
+ """
348
+ return ACCOUNT_BROWSER_LOGIN_URL
349
+
350
+
351
+ def claude_device_code_flow(
352
+ client_id: str = "claude-code-cli",
353
+ scope: str = "claude:max",
354
+ device_auth_url: str | None = None,
355
+ token_url: str | None = None,
356
+ poll_timeout: float = 600.0,
357
+ ) -> dict[str, Any]:
358
+ """RFC 8628 device authorization flow against the Claude OAuth endpoints.
359
+
360
+ Returns a dict with at least `access_token`. If the endpoint does not
361
+ support this flow (404 / 4xx / non-JSON), raises RuntimeError with a clear
362
+ message so the CLI knows to fall back to manual paste.
363
+
364
+ This function performs ZERO disk I/O — the CLI is responsible for writing
365
+ the returned token into the vault. Keeps the function pure and testable.
366
+ """
367
+ auth_url = device_auth_url or ACCOUNT_DEVICE_AUTH_URL
368
+ tok_url = token_url or ACCOUNT_DEVICE_TOKEN_URL
369
+
370
+ # ---- step 1: request a device_code ----
371
+ body = urllib.parse.urlencode({"client_id": client_id, "scope": scope}).encode()
372
+ req = urllib.request.Request(
373
+ auth_url, data=body, method="POST",
374
+ headers={"Content-Type": "application/x-www-form-urlencoded",
375
+ "Accept": "application/json"},
376
+ )
377
+ try:
378
+ with urllib.request.urlopen(req, timeout=30.0) as resp:
379
+ payload = json.loads(resp.read().decode("utf-8"))
380
+ except urllib.error.HTTPError as exc:
381
+ raise RuntimeError(
382
+ f"device authorize endpoint returned HTTP {exc.code}. "
383
+ "Anthropic may not support the device-code flow yet — "
384
+ "fall back to manual paste."
385
+ ) from exc
386
+ except (urllib.error.URLError, json.JSONDecodeError, TimeoutError) as exc:
387
+ raise RuntimeError(
388
+ f"could not reach the device authorize endpoint: {exc}. "
389
+ "Fall back to manual paste."
390
+ ) from exc
391
+
392
+ device_code = payload.get("device_code")
393
+ interval = float(payload.get("interval", 5))
394
+ expires_in = float(payload.get("expires_in", 600))
395
+ if not device_code:
396
+ raise RuntimeError(
397
+ f"device authorize response missing `device_code`: {payload!r}. "
398
+ "Fall back to manual paste."
399
+ )
400
+ verification = (
401
+ payload.get("verification_uri_complete")
402
+ or payload.get("verification_uri")
403
+ or "(see provider docs)"
404
+ )
405
+
406
+ # The caller (CLI) prints this — but in case it's invoked headlessly, surface
407
+ # it on the returned payload too.
408
+ payload["_human_instruction"] = (
409
+ f"open in a browser and approve:\n {verification}"
410
+ )
411
+
412
+ # ---- step 2: poll for the token ----
413
+ deadline = min(time.monotonic() + poll_timeout, time.monotonic() + expires_in)
414
+ poll_body = urllib.parse.urlencode({
415
+ "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
416
+ "device_code": device_code,
417
+ "client_id": client_id,
418
+ }).encode()
419
+ while time.monotonic() < deadline:
420
+ time.sleep(max(interval, 1.0))
421
+ poll_req = urllib.request.Request(
422
+ tok_url, data=poll_body, method="POST",
423
+ headers={"Content-Type": "application/x-www-form-urlencoded",
424
+ "Accept": "application/json"},
425
+ )
426
+ try:
427
+ with urllib.request.urlopen(poll_req, timeout=30.0) as resp:
428
+ tok = json.loads(resp.read().decode("utf-8"))
429
+ except urllib.error.HTTPError as exc:
430
+ try:
431
+ err_payload = json.loads(exc.read().decode("utf-8"))
432
+ except Exception: # noqa: BLE001 — best effort on body decode
433
+ err_payload = {}
434
+ err = err_payload.get("error", "")
435
+ # RFC 8628 §3.5: authorization_pending and slow_down mean keep polling.
436
+ if err == "authorization_pending":
437
+ continue
438
+ if err == "slow_down":
439
+ interval += 5
440
+ continue
441
+ raise RuntimeError(
442
+ f"device token endpoint refused: {err or exc.code} — "
443
+ f"{err_payload!r}"
444
+ ) from exc
445
+ except (urllib.error.URLError, json.JSONDecodeError, TimeoutError) as exc:
446
+ raise RuntimeError(f"could not poll token endpoint: {exc}") from exc
447
+
448
+ if tok.get("access_token"):
449
+ return {
450
+ "access_token": tok["access_token"],
451
+ "refresh_token": tok.get("refresh_token", ""),
452
+ "expires_in": int(tok.get("expires_in", 0) or 0),
453
+ "raw": tok,
454
+ }
455
+
456
+ raise RuntimeError(
457
+ "device-code flow timed out — user did not approve in time"
458
+ )
459
+
460
+
461
+ # ──── billing aggregator ─────────────────────────────────────────────────
462
+
463
+
464
+ class BillingAggregator:
465
+ """Aggregate per-account token usage from the event log.
466
+
467
+ Scans every `task.*` event whose payload includes a `usage.account_id` and
468
+ a positive `usage.input_tokens` / `usage.output_tokens`. The event log is
469
+ the source of truth — `AccountPool.tokens_used` is a cached view.
470
+ """
471
+
472
+ @staticmethod
473
+ def from_event_log(store: Any) -> dict[str, dict[str, int]]:
474
+ """Walk the store and aggregate. `store` is anything that yields
475
+ Event-like objects via `.all_events()` (see `omega_engine.store`).
476
+
477
+ Returns: { account_id: { input_tokens, output_tokens, total_calls } }
478
+ """
479
+ totals: dict[str, dict[str, int]] = {}
480
+ for ev in store.all_events():
481
+ # only billable events
482
+ etype = getattr(ev, "type", None)
483
+ type_val = etype.value if hasattr(etype, "value") else str(etype)
484
+ if not type_val.startswith("task."):
485
+ continue
486
+ payload = getattr(ev, "payload", None) or {}
487
+ usage = payload.get("usage") or {}
488
+ account_id = usage.get("account_id")
489
+ if not account_id:
490
+ continue
491
+ inp = int(usage.get("input_tokens", 0) or 0)
492
+ out = int(usage.get("output_tokens", 0) or 0)
493
+ if inp <= 0 and out <= 0:
494
+ continue
495
+ slot = totals.setdefault(
496
+ account_id,
497
+ {"input_tokens": 0, "output_tokens": 0, "total_calls": 0},
498
+ )
499
+ slot["input_tokens"] += inp
500
+ slot["output_tokens"] += out
501
+ slot["total_calls"] += 1
502
+ return totals
@@ -0,0 +1,167 @@
1
+ """Worker ↔ Oracle messaging — the missing back-channel.
2
+
3
+ Today a worker writes a single ``.done.json`` and that's it. If it's
4
+ stuck mid-task and needs the oracle's input, it has no way to ask. Then
5
+ either it guesses wrong (and the audit catches it later — wasted work)
6
+ or it gives up early (status=pending — wasted dispatch).
7
+
8
+ This primitive is a small JSON-lines file per task at
9
+ ``Agentik_Runtime/sessions/<task_id>/messages.jsonl``. The worker
10
+ appends a question, the oracle reads, appends an answer, the worker
11
+ polls and continues.
12
+
13
+ The protocol is intentionally minimal — one JSON object per line:
14
+
15
+ ``{from, to, ts, type, text}``
16
+
17
+ ``type`` is one of:
18
+
19
+ * ``question`` — worker asks the oracle
20
+ * ``answer`` — oracle's reply
21
+ * ``proposal`` — worker proposes a course change
22
+ * ``decision`` — oracle says yes/no/pivot
23
+
24
+ Polling is the operator's choice — the worker can ``read_messages``
25
+ between Bash/Edit iterations. For long-running missions the oracle's
26
+ session can sit in ``omega message wait <task_id>`` to surface
27
+ questions as they arrive.
28
+ """
29
+ from __future__ import annotations
30
+
31
+ import json
32
+ import os
33
+ import time
34
+ from dataclasses import dataclass, field
35
+ from pathlib import Path
36
+
37
+
38
+ VALID_TYPES = {"question", "answer", "proposal", "decision"}
39
+
40
+
41
+ @dataclass
42
+ class Message:
43
+ from_: str
44
+ to: str
45
+ type: str
46
+ text: str
47
+ ts: int = field(default_factory=lambda: int(time.time()))
48
+
49
+
50
+ def _msg_path(omega_home: str | Path, task_id: str) -> Path:
51
+ home = Path(omega_home)
52
+ p = home / "Agentik_Runtime" / "sessions" / task_id / "messages.jsonl"
53
+ p.parent.mkdir(parents=True, exist_ok=True)
54
+ return p
55
+
56
+
57
+ def send(
58
+ omega_home: str | Path,
59
+ task_id: str,
60
+ *,
61
+ from_: str,
62
+ to: str,
63
+ type: str,
64
+ text: str,
65
+ ) -> Message:
66
+ """Append one message to the per-task JSONL. Returns the persisted Message."""
67
+ if type not in VALID_TYPES:
68
+ raise ValueError(f"type must be one of {VALID_TYPES}, got {type!r}")
69
+ msg = Message(from_=from_, to=to, type=type, text=text)
70
+ p = _msg_path(omega_home, task_id)
71
+ with p.open("a") as fh:
72
+ fh.write(json.dumps({
73
+ "from": msg.from_, "to": msg.to,
74
+ "type": msg.type, "text": msg.text,
75
+ "ts": msg.ts,
76
+ }) + "\n")
77
+ return msg
78
+
79
+
80
+ def read(
81
+ omega_home: str | Path, task_id: str,
82
+ *,
83
+ since_ts: int = 0,
84
+ for_recipient: str | None = None,
85
+ ) -> list[Message]:
86
+ """Read messages for a task. Returns oldest-first."""
87
+ p = _msg_path(omega_home, task_id)
88
+ if not p.exists():
89
+ return []
90
+ out: list[Message] = []
91
+ for line in p.read_text().splitlines():
92
+ if not line.strip():
93
+ continue
94
+ try:
95
+ data = json.loads(line)
96
+ except json.JSONDecodeError:
97
+ continue
98
+ if int(data.get("ts", 0)) < since_ts:
99
+ continue
100
+ if for_recipient and data.get("to") != for_recipient:
101
+ continue
102
+ out.append(Message(
103
+ from_=str(data.get("from", "?")),
104
+ to=str(data.get("to", "?")),
105
+ type=str(data.get("type", "question")),
106
+ text=str(data.get("text", "")),
107
+ ts=int(data.get("ts", 0)),
108
+ ))
109
+ return out
110
+
111
+
112
+ def wait_for_reply(
113
+ omega_home: str | Path,
114
+ task_id: str,
115
+ *,
116
+ for_recipient: str,
117
+ since_ts: int | None = None,
118
+ timeout_s: int = 600,
119
+ poll_interval_s: int = 5,
120
+ ) -> Message | None:
121
+ """Block until a message addressed to ``for_recipient`` arrives or timeout.
122
+
123
+ Use case: the worker asks a question, then waits for the oracle's
124
+ answer before continuing. Returns the new message, or None on
125
+ timeout.
126
+ """
127
+ since_ts = since_ts if since_ts is not None else int(time.time())
128
+ end = time.time() + timeout_s
129
+ while time.time() < end:
130
+ msgs = read(omega_home, task_id, since_ts=since_ts,
131
+ for_recipient=for_recipient)
132
+ if msgs:
133
+ return msgs[0]
134
+ time.sleep(poll_interval_s)
135
+ return None
136
+
137
+
138
+ def list_open_questions(
139
+ omega_home: str | Path,
140
+ ) -> list[tuple[str, Message]]:
141
+ """Scan every per-task messages.jsonl for unanswered questions.
142
+
143
+ A "question" is unanswered when no later message of type=answer
144
+ addresses the same conversation. Returns (task_id, question_msg)
145
+ pairs sorted oldest first.
146
+ """
147
+ home = Path(omega_home)
148
+ sess = home / "Agentik_Runtime" / "sessions"
149
+ if not sess.is_dir():
150
+ return []
151
+ open_qs: list[tuple[str, Message]] = []
152
+ for child in sess.iterdir():
153
+ if not child.is_dir():
154
+ continue
155
+ p = child / "messages.jsonl"
156
+ if not p.exists():
157
+ continue
158
+ msgs = read(home, child.name)
159
+ # Questions whose ts is later than the latest answer in the same file
160
+ latest_answer = max(
161
+ (m.ts for m in msgs if m.type == "answer"), default=0,
162
+ )
163
+ for m in msgs:
164
+ if m.type == "question" and m.ts > latest_answer:
165
+ open_qs.append((child.name, m))
166
+ open_qs.sort(key=lambda pair: pair[1].ts)
167
+ return open_qs