@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.
- package/README.md +56 -14
- package/bootstrap/lib/__pycache__/claude-code-settings.cpython-313.pyc +0 -0
- package/bootstrap/lib/__pycache__/llm-clis.cpython-313.pyc +0 -0
- package/bootstrap/lib/__pycache__/manifest-helpers.cpython-313.pyc +0 -0
- package/bootstrap/lib/claude-code-settings.py +176 -0
- package/bootstrap/lib/common.sh +457 -1
- package/bootstrap/lib/llm-clis.py +341 -0
- package/bootstrap/lib/manifest-helpers.py +384 -0
- package/bootstrap/lib/steps.sh +1000 -26
- package/bootstrap/manifest.example.yaml +93 -2
- package/bootstrap/templates/aisb/CLAUDE.md +305 -0
- package/bootstrap/templates/aisb/architect.md +204 -0
- package/bootstrap/templates/aisb/checkers/CLAUDE.md +9 -0
- package/bootstrap/templates/aisb/checkers/checker-architect.md +151 -0
- package/bootstrap/templates/aisb/checkers/checker-common.md +171 -0
- package/bootstrap/templates/aisb/checkers/checker-construct.md +129 -0
- package/bootstrap/templates/aisb/checkers/checker-keymaker.md +204 -0
- package/bootstrap/templates/aisb/checkers/checker-link.md +205 -0
- package/bootstrap/templates/aisb/checkers/checker-merovingian.md +219 -0
- package/bootstrap/templates/aisb/checkers/checker-morpheus.md +211 -0
- package/bootstrap/templates/aisb/checkers/checker-neo.md +177 -0
- package/bootstrap/templates/aisb/checkers/checker-niobe.md +156 -0
- package/bootstrap/templates/aisb/checkers/checker-oracle.md +164 -0
- package/bootstrap/templates/aisb/checkers/checker-seraph.md +187 -0
- package/bootstrap/templates/aisb/checkers/checker-smith.md +195 -0
- package/bootstrap/templates/aisb/checkers/checker-zion.md +113 -0
- package/bootstrap/templates/aisb/construct.md +135 -0
- package/bootstrap/templates/aisb/keymaker.md +227 -0
- package/bootstrap/templates/aisb/link.md +170 -0
- package/bootstrap/templates/aisb/lmc-protocol.md +57 -0
- package/bootstrap/templates/aisb/merovingian.md +159 -0
- package/bootstrap/templates/aisb/morpheus.md +243 -0
- package/bootstrap/templates/aisb/neo.md +147 -0
- package/bootstrap/templates/aisb/niobe.md +197 -0
- package/bootstrap/templates/aisb/oracle.md +244 -0
- package/bootstrap/templates/aisb/protocols/handoff-templates.md +204 -0
- package/bootstrap/templates/aisb/protocols/shared-protocol.md +248 -0
- package/bootstrap/templates/aisb/pythia.md +153 -0
- package/bootstrap/templates/aisb/seraph.md +315 -0
- package/bootstrap/templates/aisb/smith.md +202 -0
- package/bootstrap/templates/aisb/zion.md +172 -0
- package/bootstrap/templates/autonomous/audit-patrol.yaml +41 -0
- package/bootstrap/templates/autonomous/smith-reflect.yaml +43 -0
- package/bootstrap/templates/autonomous/ssh-key-rotate.yaml +46 -0
- package/bootstrap/templates/autonomous/support-agent.yaml +38 -0
- package/docs/AUDITS.md +85 -0
- package/docs/COMPLETION-PLAN.md +48 -0
- package/docs/GAP-ANALYSIS.md +214 -0
- package/docs/INSTALL.md +47 -9
- package/docs/MCP-AND-PLUGINS.md +31 -4
- package/docs/SIMULATION.md +171 -0
- package/docs/simulate.sh +211 -0
- package/install.sh +164 -17
- package/omega/Agentik_Engine/README.md +27 -10
- package/omega/Agentik_Engine/omega_engine/__init__.py +212 -2
- package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/account.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/agent_messages.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/aisb_chat.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit_diff.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit_gate.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/auto_update.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/autonomous.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/backup.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cadence.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/classifier.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cleanup.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/completions.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/costs.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/done_signal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/envelope.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/executor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/handoff.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/hermes.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_bootstrap.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_desktop.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/learning.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/managed_agent.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/memory.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/menu.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/mission.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/plan.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/project.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/prompts.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/provider.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/prune.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/pursue.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/reducer.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/router.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/skill_routing.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/smoke.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/store.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/sync.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/telegram_history.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tools.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/understand_anything.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/updater.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/validate.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/vault.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/webhooks.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/worker.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/account.py +502 -0
- package/omega/Agentik_Engine/omega_engine/agent_messages.py +167 -0
- package/omega/Agentik_Engine/omega_engine/aisb_chat.py +128 -0
- package/omega/Agentik_Engine/omega_engine/audit_diff.py +99 -0
- package/omega/Agentik_Engine/omega_engine/audit_gate.py +149 -0
- package/omega/Agentik_Engine/omega_engine/audits/__init__.py +60 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/batcher.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/dispatcher.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/generator.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/history.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/pipeline.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/batcher.py +218 -0
- package/omega/Agentik_Engine/omega_engine/audits/dispatcher.py +92 -0
- package/omega/Agentik_Engine/omega_engine/audits/generator.py +234 -0
- package/omega/Agentik_Engine/omega_engine/audits/history.py +168 -0
- package/omega/Agentik_Engine/omega_engine/audits/pipeline.py +198 -0
- package/omega/Agentik_Engine/omega_engine/auto_update.py +339 -0
- package/omega/Agentik_Engine/omega_engine/autonomous.py +538 -0
- package/omega/Agentik_Engine/omega_engine/backup.py +215 -0
- package/omega/Agentik_Engine/omega_engine/cadence.py +158 -0
- package/omega/Agentik_Engine/omega_engine/classifier.py +215 -0
- package/omega/Agentik_Engine/omega_engine/cleanup.py +673 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +4564 -56
- package/omega/Agentik_Engine/omega_engine/completions.py +260 -0
- package/omega/Agentik_Engine/omega_engine/costs.py +100 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__init__.py +14 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/autonomous.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/engine.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/telegram.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/autonomous.py +56 -0
- package/omega/Agentik_Engine/omega_engine/daemons/engine.py +236 -0
- package/omega/Agentik_Engine/omega_engine/daemons/telegram.py +315 -0
- package/omega/Agentik_Engine/omega_engine/done_signal.py +154 -0
- package/omega/Agentik_Engine/omega_engine/educators/__init__.py +51 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/artifact.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/automation.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/base.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/claudecode.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/connection.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/coworker.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/loop.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/prompt.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/skill.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/artifact.py +65 -0
- package/omega/Agentik_Engine/omega_engine/educators/automation.py +76 -0
- package/omega/Agentik_Engine/omega_engine/educators/base.py +327 -0
- package/omega/Agentik_Engine/omega_engine/educators/claudecode.py +71 -0
- package/omega/Agentik_Engine/omega_engine/educators/connection.py +75 -0
- package/omega/Agentik_Engine/omega_engine/educators/coworker.py +68 -0
- package/omega/Agentik_Engine/omega_engine/educators/loop.py +82 -0
- package/omega/Agentik_Engine/omega_engine/educators/prompt.py +68 -0
- package/omega/Agentik_Engine/omega_engine/educators/skill.py +69 -0
- package/omega/Agentik_Engine/omega_engine/envelope.py +219 -0
- package/omega/Agentik_Engine/omega_engine/executor.py +195 -16
- package/omega/Agentik_Engine/omega_engine/genesis/__init__.py +134 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/phases.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/stack.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/state.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/orchestrator.py +262 -0
- package/omega/Agentik_Engine/omega_engine/genesis/phases.py +950 -0
- package/omega/Agentik_Engine/omega_engine/genesis/stack.py +324 -0
- package/omega/Agentik_Engine/omega_engine/genesis/state.py +353 -0
- package/omega/Agentik_Engine/omega_engine/handoff.py +459 -0
- package/omega/Agentik_Engine/omega_engine/hermes.py +426 -0
- package/omega/Agentik_Engine/omega_engine/hermes_bootstrap.py +382 -0
- package/omega/Agentik_Engine/omega_engine/hermes_desktop.py +469 -0
- package/omega/Agentik_Engine/omega_engine/integrations/__init__.py +30 -0
- package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/graphify.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/integrations/graphify.py +234 -0
- package/omega/Agentik_Engine/omega_engine/learning.py +268 -0
- package/omega/Agentik_Engine/omega_engine/managed_agent.py +467 -0
- package/omega/Agentik_Engine/omega_engine/memory.py +271 -0
- package/omega/Agentik_Engine/omega_engine/menu.py +1065 -0
- package/omega/Agentik_Engine/omega_engine/migrations/__init__.py +144 -0
- package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/v0_14_0.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/migrations/v0_14_0.py +29 -0
- package/omega/Agentik_Engine/omega_engine/mission.py +29 -14
- package/omega/Agentik_Engine/omega_engine/plan.py +846 -0
- package/omega/Agentik_Engine/omega_engine/prompts.py +158 -0
- package/omega/Agentik_Engine/omega_engine/provider.py +408 -13
- package/omega/Agentik_Engine/omega_engine/prune.py +151 -0
- package/omega/Agentik_Engine/omega_engine/pursue.py +205 -0
- package/omega/Agentik_Engine/omega_engine/rag/__init__.py +21 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/agentic.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/base.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/corrective.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/graph.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/hybrid.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/multimodal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/router.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/agentic.py +83 -0
- package/omega/Agentik_Engine/omega_engine/rag/base.py +42 -0
- package/omega/Agentik_Engine/omega_engine/rag/corrective.py +119 -0
- package/omega/Agentik_Engine/omega_engine/rag/graph.py +169 -0
- package/omega/Agentik_Engine/omega_engine/rag/hybrid.py +205 -0
- package/omega/Agentik_Engine/omega_engine/rag/multimodal.py +136 -0
- package/omega/Agentik_Engine/omega_engine/rag/router.py +110 -0
- package/omega/Agentik_Engine/omega_engine/reducer.py +21 -3
- package/omega/Agentik_Engine/omega_engine/router.py +28 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__init__.py +48 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/auditor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/finder.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/installer.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/marketplaces.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/auditor.py +232 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/finder.py +94 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/installer.py +129 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/marketplaces.py +80 -0
- package/omega/Agentik_Engine/omega_engine/skill_routing.py +388 -0
- package/omega/Agentik_Engine/omega_engine/smoke.py +81 -0
- package/omega/Agentik_Engine/omega_engine/store.py +132 -25
- package/omega/Agentik_Engine/omega_engine/sync.py +445 -0
- package/omega/Agentik_Engine/omega_engine/telegram_history.py +260 -0
- package/omega/Agentik_Engine/omega_engine/tmux.py +526 -0
- package/omega/Agentik_Engine/omega_engine/tools.py +272 -0
- package/omega/Agentik_Engine/omega_engine/understand_anything.py +275 -0
- package/omega/Agentik_Engine/omega_engine/updater.py +70 -0
- package/omega/Agentik_Engine/omega_engine/validate.py +186 -0
- package/omega/Agentik_Engine/omega_engine/vault.py +342 -0
- package/omega/Agentik_Engine/omega_engine/webhooks.py +262 -0
- package/omega/Agentik_Engine/omega_engine/worker.py +526 -0
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audit_arsenal.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_executor.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_mission.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_progress.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_project.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_reducer.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_report.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/test_account.py +338 -0
- package/omega/Agentik_Engine/tests/test_adversarial.py +351 -0
- package/omega/Agentik_Engine/tests/test_agents_envelope.py +274 -0
- package/omega/Agentik_Engine/tests/test_audits_pipeline.py +348 -0
- package/omega/Agentik_Engine/tests/test_auto_update_and_migrations.py +394 -0
- package/omega/Agentik_Engine/tests/test_autonomous.py +361 -0
- package/omega/Agentik_Engine/tests/test_educators.py +233 -0
- package/omega/Agentik_Engine/tests/test_genesis_and_plan.py +573 -0
- package/omega/Agentik_Engine/tests/test_graphify.py +190 -0
- package/omega/Agentik_Engine/tests/test_handoff.py +311 -0
- package/omega/Agentik_Engine/tests/test_hermes_and_ua.py +387 -0
- package/omega/Agentik_Engine/tests/test_hermes_bootstrap_and_desktop.py +358 -0
- package/omega/Agentik_Engine/tests/test_install_steps.py +359 -0
- package/omega/Agentik_Engine/tests/test_install_ux.py +151 -0
- package/omega/Agentik_Engine/tests/test_installer_wiring.py +496 -0
- package/omega/Agentik_Engine/tests/test_intelligence.py +285 -0
- package/omega/Agentik_Engine/tests/test_llm_clis_and_uninstall.py +228 -0
- package/omega/Agentik_Engine/tests/test_managed_agent.py +363 -0
- package/omega/Agentik_Engine/tests/test_max_provider_and_menu.py +231 -0
- package/omega/Agentik_Engine/tests/test_menu_coverage.py +72 -0
- package/omega/Agentik_Engine/tests/test_pursue_cadence.py +217 -0
- package/omega/Agentik_Engine/tests/test_rag.py +287 -0
- package/omega/Agentik_Engine/tests/test_role_aliases_and_ssot.py +207 -0
- package/omega/Agentik_Engine/tests/test_skill_discovery_and_gate.py +337 -0
- package/omega/Agentik_Engine/tests/test_skill_power.py +259 -0
- package/omega/Agentik_Engine/tests/test_skill_routing.py +189 -0
- package/omega/Agentik_Engine/tests/test_snapshot_partial.py +172 -0
- package/omega/Agentik_Engine/tests/test_telegram_history.py +209 -0
- package/omega/Agentik_Engine/tests/test_tmux_and_aisb_chat.py +223 -0
- package/omega/Agentik_Engine/tests/test_tools_and_sync.py +312 -0
- package/omega/Agentik_Engine/tests/test_v06_features.py +370 -0
- package/omega/Agentik_Engine/tests/test_vault.py +173 -0
- package/omega/Agentik_Engine/tests/test_webhooks_and_readiness.py +277 -0
- package/omega/Agentik_Engine/tests/test_worker_and_cleanup.py +541 -0
- package/omega/Agentik_Extra/etc/secrets/.vault-key +3 -0
- package/omega/Agentik_Extra/etc/secrets/.vault-pub +1 -0
- package/omega/Agentik_Runtime/audits.db +0 -0
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/omega/Agentik_SSOT/claude-plugins/claude-plugins.yaml +100 -0
- package/omega/Agentik_SSOT/docs/LAYERS.md +90 -0
- package/omega/Agentik_SSOT/docs/USER-JOURNEY.md +283 -0
- package/omega/Agentik_SSOT/marketplaces/design-discipline.yaml +86 -0
- package/omega/Agentik_SSOT/skills/a11yaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/apiaudit/SKILL.md +157 -0
- package/omega/Agentik_SSOT/skills/automationaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/cadence/SKILL.md +76 -0
- package/omega/Agentik_SSOT/skills/codeaudit/SKILL.md +153 -0
- package/omega/Agentik_SSOT/skills/copyaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/dataaudit/SKILL.md +157 -0
- package/omega/Agentik_SSOT/skills/debugaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/dispatch/SKILL.md +79 -0
- package/omega/Agentik_SSOT/skills/dxaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/featureaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/flowaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/genesis/SKILL.md +116 -0
- package/omega/Agentik_SSOT/skills/handoff/SKILL.md +117 -0
- package/omega/Agentik_SSOT/skills/logicaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/motionaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/perfaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/plan/SKILL.md +127 -0
- package/omega/Agentik_SSOT/skills/pursue/SKILL.md +68 -0
- package/omega/Agentik_SSOT/skills/rag-route.md +82 -0
- package/omega/Agentik_SSOT/skills/refontaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/retentionaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/secaudit/SKILL.md +157 -0
- package/omega/Agentik_SSOT/skills/seoaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/skill-auditor/SKILL.md +83 -0
- package/omega/Agentik_SSOT/skills/skill-finder/SKILL.md +116 -0
- package/omega/Agentik_SSOT/skills/uiuxaudit/SKILL.md +165 -0
- package/package.json +2 -2
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
"""SST → provider projection — the `omega sync` engine.
|
|
2
|
+
|
|
3
|
+
The SSOT is provider-neutral. Each provider adapter compiles
|
|
4
|
+
`Agentik_SSOT/` into that provider's native shape. Re-running `omega sync` is
|
|
5
|
+
idempotent: same SSOT in, same projection out — so the SSOT remains the only
|
|
6
|
+
hand-edited surface.
|
|
7
|
+
|
|
8
|
+
Today only the Claude Code adapter is fully wired. GLM / OpenAI / DeepSeek
|
|
9
|
+
providers consume the SSOT directly via API calls and have no on-disk native
|
|
10
|
+
layout, so their adapters log a no-op message. New providers add one adapter
|
|
11
|
+
file; the SSOT does not change.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Iterable, Protocol, runtime_checkable
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _omega_home(explicit: str | Path | None = None) -> Path:
|
|
22
|
+
return Path(explicit or os.environ.get("OMEGA_HOME", str(Path.home() / "Omega")))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# --------------------------------------------------------------------------
|
|
26
|
+
# Provider adapter contract
|
|
27
|
+
# --------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@runtime_checkable
|
|
31
|
+
class ProviderAdapter(Protocol):
|
|
32
|
+
"""Each provider exposes a `project(omega_home)` that writes its native layout."""
|
|
33
|
+
|
|
34
|
+
id: str
|
|
35
|
+
|
|
36
|
+
def project(self, omega_home: Path) -> dict[str, Any]: ...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class _StubAdapter:
|
|
40
|
+
"""Adapter that has no on-disk native layout — logs and returns metadata."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, provider_id: str, reason: str) -> None:
|
|
43
|
+
self.id = provider_id
|
|
44
|
+
self._reason = reason
|
|
45
|
+
|
|
46
|
+
def project(self, omega_home: Path) -> dict[str, Any]:
|
|
47
|
+
msg = f"[sync] {self.id}: {self._reason}"
|
|
48
|
+
print(msg)
|
|
49
|
+
return {"provider": self.id, "projected": False, "reason": self._reason}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# --------------------------------------------------------------------------
|
|
53
|
+
# Claude Code adapter — the only fully wired one for now
|
|
54
|
+
# --------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ClaudeCodeAdapter:
|
|
58
|
+
"""Project the SSOT into Claude Code's native `.claude/` layout.
|
|
59
|
+
|
|
60
|
+
Native target tree (idempotent):
|
|
61
|
+
<target>/skills/<id>/SKILL.md
|
|
62
|
+
<target>/commands/<id>.md
|
|
63
|
+
<target>/agents/<id>.md
|
|
64
|
+
<target>/.mcp.json { "mcpServers": { ... } }
|
|
65
|
+
<target>/settings.json { ..., "hooks": {...} }
|
|
66
|
+
|
|
67
|
+
The SSOT is never modified — the adapter is one-way (SSOT → projection).
|
|
68
|
+
Default target: `<omega_home>/Agentik_AI/providers/claude-code/.claude/`.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
id = "claude-code"
|
|
72
|
+
|
|
73
|
+
def __init__(self, target: str | Path | None = None) -> None:
|
|
74
|
+
self._explicit_target = Path(target) if target else None
|
|
75
|
+
|
|
76
|
+
# ----- helpers -------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def target_dir(self, omega_home: Path) -> Path:
|
|
79
|
+
if self._explicit_target is not None:
|
|
80
|
+
return self._explicit_target
|
|
81
|
+
return omega_home / "Agentik_AI" / "providers" / "claude-code" / ".claude"
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def _ensure(d: Path) -> Path:
|
|
85
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
return d
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def _stem(path: Path) -> str:
|
|
90
|
+
return path.stem
|
|
91
|
+
|
|
92
|
+
# ----- skills / commands / agents (markdown surfaces) ----------------
|
|
93
|
+
|
|
94
|
+
def _project_markdown_dir(
|
|
95
|
+
self,
|
|
96
|
+
src: Path,
|
|
97
|
+
out_root: Path,
|
|
98
|
+
layout: str, # "skill" | "command" | "agent"
|
|
99
|
+
) -> list[str]:
|
|
100
|
+
"""Copy `src/*.md` AND `src/<name>/SKILL.md` to the native layout.
|
|
101
|
+
|
|
102
|
+
Both shapes are supported in the SSOT:
|
|
103
|
+
1. Flat: ``Agentik_SSOT/skills/pursue.md``
|
|
104
|
+
2. Directory: ``Agentik_SSOT/skills/pursue/SKILL.md`` (+ optional
|
|
105
|
+
supporting files like ``scripts/`` or ``examples/``).
|
|
106
|
+
|
|
107
|
+
Directory-based skills are the Claude Code idiom — supporting files
|
|
108
|
+
sit next to ``SKILL.md`` and the agent can reference them via
|
|
109
|
+
``${CLAUDE_SKILL_DIR}``. We copy the whole directory verbatim so
|
|
110
|
+
those references resolve at the destination.
|
|
111
|
+
|
|
112
|
+
Returns the list of written paths (relative to ``out_root.parent``).
|
|
113
|
+
"""
|
|
114
|
+
if not src.is_dir():
|
|
115
|
+
return []
|
|
116
|
+
out_root = self._ensure(out_root)
|
|
117
|
+
written: list[str] = []
|
|
118
|
+
|
|
119
|
+
# 1. flat: src/<name>.md
|
|
120
|
+
for md in sorted(src.glob("*.md")):
|
|
121
|
+
stem = self._stem(md)
|
|
122
|
+
if layout == "skill":
|
|
123
|
+
dest_dir = self._ensure(out_root / stem)
|
|
124
|
+
dest = dest_dir / "SKILL.md"
|
|
125
|
+
else:
|
|
126
|
+
dest = out_root / f"{stem}.md"
|
|
127
|
+
dest.write_text(md.read_text())
|
|
128
|
+
written.append(str(dest.relative_to(out_root.parent)))
|
|
129
|
+
|
|
130
|
+
# 2. directory: src/<name>/SKILL.md (+ supporting files)
|
|
131
|
+
if layout == "skill":
|
|
132
|
+
import shutil as _sh
|
|
133
|
+
for child in sorted(src.iterdir()):
|
|
134
|
+
if not child.is_dir():
|
|
135
|
+
continue
|
|
136
|
+
skill_md = child / "SKILL.md"
|
|
137
|
+
if not skill_md.exists():
|
|
138
|
+
continue
|
|
139
|
+
dest_dir = self._ensure(out_root / child.name)
|
|
140
|
+
# Copy SKILL.md plus any supporting files (scripts/, etc.)
|
|
141
|
+
for item in child.iterdir():
|
|
142
|
+
target = dest_dir / item.name
|
|
143
|
+
if item.is_dir():
|
|
144
|
+
if target.exists():
|
|
145
|
+
_sh.rmtree(target)
|
|
146
|
+
_sh.copytree(item, target)
|
|
147
|
+
else:
|
|
148
|
+
target.write_text(item.read_text())
|
|
149
|
+
written.append(
|
|
150
|
+
str((dest_dir / "SKILL.md").relative_to(out_root.parent))
|
|
151
|
+
)
|
|
152
|
+
return written
|
|
153
|
+
|
|
154
|
+
# ----- MCP config (.mcp.json) ---------------------------------------
|
|
155
|
+
|
|
156
|
+
def _project_mcp(self, omega_home: Path, out_root: Path) -> dict[str, Any]:
|
|
157
|
+
"""Compile mcp-config.yaml + the tool registry → Claude Code .mcp.json.
|
|
158
|
+
|
|
159
|
+
Claude Code expects: { "mcpServers": { "<id>": { "command": "...",
|
|
160
|
+
"args": [...],
|
|
161
|
+
"env": {...} } } }
|
|
162
|
+
We resolve `command` from the tool registry invoke path when available.
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
import yaml
|
|
166
|
+
except ImportError:
|
|
167
|
+
return {}
|
|
168
|
+
|
|
169
|
+
cfg_path = omega_home / "Agentik_SSOT" / "mcp" / "mcp-config.yaml"
|
|
170
|
+
catalog_path = omega_home / "Agentik_SSOT" / "mcp" / "mcp-catalog.yaml"
|
|
171
|
+
|
|
172
|
+
cfg: dict[str, Any] = {}
|
|
173
|
+
if cfg_path.exists():
|
|
174
|
+
cfg = yaml.safe_load(cfg_path.read_text()) or {}
|
|
175
|
+
|
|
176
|
+
catalog_by_id: dict[str, dict[str, Any]] = {}
|
|
177
|
+
if catalog_path.exists():
|
|
178
|
+
catalog = yaml.safe_load(catalog_path.read_text()) or {}
|
|
179
|
+
for e in catalog.get("catalog") or []:
|
|
180
|
+
catalog_by_id[str(e.get("id"))] = e
|
|
181
|
+
|
|
182
|
+
# The tool registry tells us where the binary is for each MCP id.
|
|
183
|
+
from omega_engine.tools import ToolRegistry # local import — avoid cycle
|
|
184
|
+
reg = ToolRegistry.load(omega_home)
|
|
185
|
+
tool_by_id = {t.name: t for t in reg.list()}
|
|
186
|
+
|
|
187
|
+
mcp_servers: dict[str, Any] = {}
|
|
188
|
+
for server in cfg.get("servers") or []:
|
|
189
|
+
sid = str(server.get("id", "")).strip()
|
|
190
|
+
if not sid or not server.get("enabled", True):
|
|
191
|
+
continue
|
|
192
|
+
entry: dict[str, Any] = {}
|
|
193
|
+
tool = tool_by_id.get(sid)
|
|
194
|
+
cat = catalog_by_id.get(sid, {})
|
|
195
|
+
install = cat.get("install") or {}
|
|
196
|
+
|
|
197
|
+
# Prefer the locally installed binary; fall back to `npx <package>`.
|
|
198
|
+
if tool and tool.invoke:
|
|
199
|
+
entry["command"] = str(omega_home / tool.invoke)
|
|
200
|
+
entry["args"] = list(server.get("args") or [])
|
|
201
|
+
elif install.get("method") == "npx" and install.get("package"):
|
|
202
|
+
entry["command"] = "npx"
|
|
203
|
+
entry["args"] = ["-y", str(install["package"]), *(server.get("args") or [])]
|
|
204
|
+
else:
|
|
205
|
+
# nothing to invoke — skip rather than write a half-broken entry
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
from_server = list(server.get("secret_refs") or [])
|
|
209
|
+
from_tool = list(tool.secret_refs) if tool else []
|
|
210
|
+
secret_refs = from_server or from_tool
|
|
211
|
+
if secret_refs:
|
|
212
|
+
# Claude Code's .mcp.json supports an `env` block; we project
|
|
213
|
+
# secret refs by name (the value is filled at runtime from the
|
|
214
|
+
# vault, never written to disk in plaintext).
|
|
215
|
+
entry["env"] = {ref: f"${{{ref}}}" for ref in secret_refs}
|
|
216
|
+
|
|
217
|
+
mcp_servers[sid] = entry
|
|
218
|
+
|
|
219
|
+
out_root = self._ensure(out_root)
|
|
220
|
+
target = out_root / ".mcp.json"
|
|
221
|
+
payload = {"mcpServers": mcp_servers}
|
|
222
|
+
target.write_text(json.dumps(payload, indent=2) + "\n")
|
|
223
|
+
return payload
|
|
224
|
+
|
|
225
|
+
# ----- hooks → settings.json -----------------------------------------
|
|
226
|
+
|
|
227
|
+
def _project_hooks(self, omega_home: Path, out_root: Path) -> dict[str, Any]:
|
|
228
|
+
"""Merge `Agentik_SSOT/hooks/*` into <target>/settings.json `hooks`.
|
|
229
|
+
|
|
230
|
+
Each file under hooks/ is one of:
|
|
231
|
+
- *.json — already in Claude Code hook shape; merged shallow into hooks
|
|
232
|
+
- *.yaml/*.yml — same shape, parsed as YAML
|
|
233
|
+
Unknown extensions are ignored.
|
|
234
|
+
"""
|
|
235
|
+
hooks_dir = omega_home / "Agentik_SSOT" / "hooks"
|
|
236
|
+
out_root = self._ensure(out_root)
|
|
237
|
+
settings_file = out_root / "settings.json"
|
|
238
|
+
|
|
239
|
+
settings: dict[str, Any] = {}
|
|
240
|
+
if settings_file.exists():
|
|
241
|
+
try:
|
|
242
|
+
settings = json.loads(settings_file.read_text() or "{}")
|
|
243
|
+
except json.JSONDecodeError:
|
|
244
|
+
settings = {}
|
|
245
|
+
|
|
246
|
+
hooks_block: dict[str, Any] = dict(settings.get("hooks") or {})
|
|
247
|
+
|
|
248
|
+
if hooks_dir.is_dir():
|
|
249
|
+
import yaml
|
|
250
|
+
for path in sorted(hooks_dir.iterdir()):
|
|
251
|
+
if not path.is_file():
|
|
252
|
+
continue
|
|
253
|
+
if path.suffix == ".json":
|
|
254
|
+
try:
|
|
255
|
+
data = json.loads(path.read_text() or "{}")
|
|
256
|
+
except json.JSONDecodeError:
|
|
257
|
+
continue
|
|
258
|
+
elif path.suffix in {".yaml", ".yml"}:
|
|
259
|
+
data = yaml.safe_load(path.read_text() or "") or {}
|
|
260
|
+
else:
|
|
261
|
+
continue
|
|
262
|
+
if not isinstance(data, dict):
|
|
263
|
+
continue
|
|
264
|
+
for k, v in data.items():
|
|
265
|
+
hooks_block[k] = v
|
|
266
|
+
|
|
267
|
+
settings["hooks"] = hooks_block
|
|
268
|
+
settings_file.write_text(json.dumps(settings, indent=2) + "\n")
|
|
269
|
+
return hooks_block
|
|
270
|
+
|
|
271
|
+
# ----- bridge to ~/.claude/ (the dir Claude Code actually reads) -----
|
|
272
|
+
#
|
|
273
|
+
# Why this exists
|
|
274
|
+
# ---------------
|
|
275
|
+
# ``project()`` writes the SSOT-derived layout into
|
|
276
|
+
# ``$OMEGA_HOME/Agentik_AI/providers/claude-code/.claude/`` — that tree
|
|
277
|
+
# stays under OmegaOS for portability (vault backup, audit trail, you
|
|
278
|
+
# name it). But the ``claude`` CLI on a real workstation reads skills /
|
|
279
|
+
# commands / agents / .mcp.json from ``~/.claude/`` (user-global) and
|
|
280
|
+
# ``.claude/`` (project-local).
|
|
281
|
+
#
|
|
282
|
+
# Without a bridge, the 24 SSOT skills NEVER become invokable slash
|
|
283
|
+
# commands. ``link_to_user_claude(home)`` is that bridge: for every
|
|
284
|
+
# skill we projected, we create a SYMLINK at
|
|
285
|
+
# ``~/.claude/skills/<id> → $OMEGA_HOME/.../skills/<id>``. This means:
|
|
286
|
+
# - editing the SSOT + re-running ``omega sync`` updates the
|
|
287
|
+
# skill in place (no duplicate copies),
|
|
288
|
+
# - removing the SSOT skill nukes the symlink on the next sync,
|
|
289
|
+
# - the operator's existing ``~/.claude/skills/<other>/`` are
|
|
290
|
+
# untouched (we only manage entries whose targets are inside
|
|
291
|
+
# ``$OMEGA_HOME``).
|
|
292
|
+
|
|
293
|
+
def link_to_user_claude(
|
|
294
|
+
self, omega_home: Path,
|
|
295
|
+
*,
|
|
296
|
+
user_claude: Path | None = None,
|
|
297
|
+
) -> dict[str, Any]:
|
|
298
|
+
"""Bridge the projected tree into ``~/.claude/`` via symlinks.
|
|
299
|
+
|
|
300
|
+
Returns ``{linked_skills, linked_commands, linked_agents,
|
|
301
|
+
removed: [stale_paths]}``. Idempotent.
|
|
302
|
+
"""
|
|
303
|
+
import os as _os
|
|
304
|
+
user = Path(user_claude) if user_claude else Path.home() / ".claude"
|
|
305
|
+
target = self.target_dir(omega_home)
|
|
306
|
+
out: dict[str, Any] = {
|
|
307
|
+
"user_claude": str(user),
|
|
308
|
+
"linked_skills": 0,
|
|
309
|
+
"linked_commands": 0,
|
|
310
|
+
"linked_agents": 0,
|
|
311
|
+
"removed": [],
|
|
312
|
+
}
|
|
313
|
+
# The provider-tree target must exist first (project() was called).
|
|
314
|
+
if not target.is_dir():
|
|
315
|
+
return out
|
|
316
|
+
|
|
317
|
+
for layout in ("skills", "commands", "agents"):
|
|
318
|
+
src_root = target / layout
|
|
319
|
+
if not src_root.is_dir():
|
|
320
|
+
continue
|
|
321
|
+
dest_root = self._ensure(user / layout)
|
|
322
|
+
|
|
323
|
+
# Remove stale symlinks we used to manage. A "managed" symlink
|
|
324
|
+
# points anywhere inside `target`. Real dirs and symlinks owned
|
|
325
|
+
# by the user are NEVER touched.
|
|
326
|
+
for child in list(dest_root.iterdir()):
|
|
327
|
+
if not child.is_symlink():
|
|
328
|
+
continue
|
|
329
|
+
try:
|
|
330
|
+
realpath = child.resolve()
|
|
331
|
+
except OSError:
|
|
332
|
+
continue
|
|
333
|
+
# Only nuke our own — points inside the OmegaOS tree.
|
|
334
|
+
if str(realpath).startswith(str(target)) and not (src_root / child.name).exists():
|
|
335
|
+
try:
|
|
336
|
+
child.unlink()
|
|
337
|
+
out["removed"].append(str(child))
|
|
338
|
+
except OSError:
|
|
339
|
+
pass
|
|
340
|
+
|
|
341
|
+
for src in sorted(src_root.iterdir()):
|
|
342
|
+
if not src.is_dir() and not src.suffix == ".md":
|
|
343
|
+
continue
|
|
344
|
+
dest = dest_root / src.name
|
|
345
|
+
if dest.is_symlink():
|
|
346
|
+
try:
|
|
347
|
+
dest.unlink()
|
|
348
|
+
except OSError:
|
|
349
|
+
pass
|
|
350
|
+
elif dest.exists():
|
|
351
|
+
# Don't clobber the user's hand-written content.
|
|
352
|
+
continue
|
|
353
|
+
try:
|
|
354
|
+
_os.symlink(src, dest)
|
|
355
|
+
out[f"linked_{layout}"] += 1
|
|
356
|
+
except OSError:
|
|
357
|
+
# Some filesystems (Windows WSL crossings) refuse symlinks;
|
|
358
|
+
# in that case the user can re-run with --copy.
|
|
359
|
+
pass
|
|
360
|
+
return out
|
|
361
|
+
|
|
362
|
+
# ----- entry point ---------------------------------------------------
|
|
363
|
+
|
|
364
|
+
def project(self, omega_home: Path) -> dict[str, Any]:
|
|
365
|
+
target = self.target_dir(omega_home)
|
|
366
|
+
self._ensure(target)
|
|
367
|
+
ssot = omega_home / "Agentik_SSOT"
|
|
368
|
+
|
|
369
|
+
skills = self._project_markdown_dir(ssot / "skills", target / "skills", "skill")
|
|
370
|
+
commands = self._project_markdown_dir(ssot / "commands", target / "commands", "command")
|
|
371
|
+
agents = self._project_markdown_dir(ssot / "agents", target / "agents", "agent")
|
|
372
|
+
mcp = self._project_mcp(omega_home, target)
|
|
373
|
+
hooks = self._project_hooks(omega_home, target)
|
|
374
|
+
|
|
375
|
+
# Bridge to ~/.claude/ so the projected skills actually become
|
|
376
|
+
# invokable slash-commands. Toggleable via env var (off → only the
|
|
377
|
+
# provider tree is written; user opts in later with `omega skill link`).
|
|
378
|
+
link_info: dict[str, Any] = {}
|
|
379
|
+
import os as _os
|
|
380
|
+
if _os.environ.get("OMEGA_SYNC_NO_LINK") != "1":
|
|
381
|
+
try:
|
|
382
|
+
link_info = self.link_to_user_claude(omega_home)
|
|
383
|
+
except OSError:
|
|
384
|
+
link_info = {}
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
"provider": self.id,
|
|
388
|
+
"projected": True,
|
|
389
|
+
"target": str(target),
|
|
390
|
+
"skills_written": len(skills),
|
|
391
|
+
"commands_written": len(commands),
|
|
392
|
+
"agents_written": len(agents),
|
|
393
|
+
"mcp_servers": len(mcp.get("mcpServers", {})) if isinstance(mcp, dict) else 0,
|
|
394
|
+
"hooks": list(hooks.keys()),
|
|
395
|
+
"linked_to_user_claude": link_info,
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
# --------------------------------------------------------------------------
|
|
400
|
+
# Sync engine
|
|
401
|
+
# --------------------------------------------------------------------------
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _active_providers(omega_home: Path) -> list[str]:
|
|
405
|
+
"""Read Agentik_Providers/registry.yaml; default to [claude] if missing."""
|
|
406
|
+
try:
|
|
407
|
+
import yaml
|
|
408
|
+
except ImportError:
|
|
409
|
+
return ["claude"]
|
|
410
|
+
path = omega_home / "Agentik_Providers" / "registry.yaml"
|
|
411
|
+
if not path.exists():
|
|
412
|
+
return ["claude"]
|
|
413
|
+
data = yaml.safe_load(path.read_text()) or {}
|
|
414
|
+
return [str(p.get("id")) for p in data.get("providers") or [] if p.get("id")]
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class SyncEngine:
|
|
418
|
+
"""Run every active provider's adapter against the SSOT."""
|
|
419
|
+
|
|
420
|
+
def __init__(self, adapters: Iterable[ProviderAdapter] | None = None) -> None:
|
|
421
|
+
self._explicit = list(adapters) if adapters is not None else None
|
|
422
|
+
|
|
423
|
+
def adapters_for(self, omega_home: Path) -> list[ProviderAdapter]:
|
|
424
|
+
if self._explicit is not None:
|
|
425
|
+
return list(self._explicit)
|
|
426
|
+
active = _active_providers(omega_home)
|
|
427
|
+
result: list[ProviderAdapter] = []
|
|
428
|
+
for pid in active:
|
|
429
|
+
if pid == "claude":
|
|
430
|
+
result.append(ClaudeCodeAdapter())
|
|
431
|
+
else:
|
|
432
|
+
# GLM / OpenAI / DeepSeek don't have a native on-disk format;
|
|
433
|
+
# MCP/skills/commands are passed via API. Document this.
|
|
434
|
+
result.append(_StubAdapter(
|
|
435
|
+
pid,
|
|
436
|
+
"no native projection — used directly via API",
|
|
437
|
+
))
|
|
438
|
+
return result
|
|
439
|
+
|
|
440
|
+
def sync_all(self, omega_home: str | Path | None = None) -> list[dict[str, Any]]:
|
|
441
|
+
home = _omega_home(omega_home)
|
|
442
|
+
outcomes: list[dict[str, Any]] = []
|
|
443
|
+
for adapter in self.adapters_for(home):
|
|
444
|
+
outcomes.append(adapter.project(home))
|
|
445
|
+
return outcomes
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Per-topic conversation history — fixes the "bot has no memory" bug.
|
|
2
|
+
|
|
3
|
+
When the user types in a Telegram topic, the Telegram daemon used to call
|
|
4
|
+
``run_mission(intent=text)`` with ONLY the new message. The Oracle saw a
|
|
5
|
+
brand-new mission every turn — no idea what the user was actually talking
|
|
6
|
+
about. The user's reply to a follow-up question landed without context.
|
|
7
|
+
|
|
8
|
+
This module is the fix. It persists every inbound + outbound message per
|
|
9
|
+
topic to a SQLite table; the daemon prepends the last N exchanges to the
|
|
10
|
+
envelope's user message so the agent always has the conversation.
|
|
11
|
+
|
|
12
|
+
Layout
|
|
13
|
+
------
|
|
14
|
+
``$OMEGA_HOME/Agentik_Runtime/telegram-history.db``
|
|
15
|
+
|
|
16
|
+
::
|
|
17
|
+
|
|
18
|
+
CREATE TABLE messages (
|
|
19
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
+
topic_id INTEGER NOT NULL, -- forum topic id (or 0 for DM root)
|
|
21
|
+
role TEXT NOT NULL, -- 'user' | 'bot'
|
|
22
|
+
text TEXT NOT NULL,
|
|
23
|
+
message_id INTEGER, -- Telegram's own message_id
|
|
24
|
+
timestamp INTEGER NOT NULL
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
Two indexes: one for per-topic recency, one for full-text-ish search.
|
|
28
|
+
|
|
29
|
+
Public API
|
|
30
|
+
----------
|
|
31
|
+
``record_inbound(home, topic_id, text, message_id=None)``
|
|
32
|
+
``record_outbound(home, topic_id, text, message_id=None)``
|
|
33
|
+
``recent_messages(home, topic_id, limit=20) -> list[Message]``
|
|
34
|
+
``build_context_prompt(home, topic_id, new_intent, limit=10) -> str``
|
|
35
|
+
"""
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import os
|
|
39
|
+
import sqlite3
|
|
40
|
+
import time
|
|
41
|
+
from dataclasses import dataclass, field
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
from typing import Iterable
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class Message:
|
|
48
|
+
id: int
|
|
49
|
+
topic_id: int
|
|
50
|
+
role: str # "user" | "bot"
|
|
51
|
+
text: str
|
|
52
|
+
message_id: int | None
|
|
53
|
+
timestamp: int
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _db_path(omega_home: str | Path) -> Path:
|
|
57
|
+
home = Path(omega_home)
|
|
58
|
+
runtime = home / "Agentik_Runtime"
|
|
59
|
+
runtime.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
return runtime / "telegram-history.db"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _connect(omega_home: str | Path) -> sqlite3.Connection:
|
|
64
|
+
conn = sqlite3.connect(str(_db_path(omega_home)))
|
|
65
|
+
conn.row_factory = sqlite3.Row
|
|
66
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
67
|
+
conn.execute("""
|
|
68
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
69
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
70
|
+
topic_id INTEGER NOT NULL,
|
|
71
|
+
role TEXT NOT NULL CHECK(role IN ('user', 'bot')),
|
|
72
|
+
text TEXT NOT NULL,
|
|
73
|
+
message_id INTEGER,
|
|
74
|
+
timestamp INTEGER NOT NULL
|
|
75
|
+
)
|
|
76
|
+
""")
|
|
77
|
+
conn.execute("""
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_messages_topic_time
|
|
79
|
+
ON messages(topic_id, timestamp DESC)
|
|
80
|
+
""")
|
|
81
|
+
return conn
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _record(
|
|
85
|
+
omega_home: str | Path,
|
|
86
|
+
*,
|
|
87
|
+
topic_id: int,
|
|
88
|
+
role: str,
|
|
89
|
+
text: str,
|
|
90
|
+
message_id: int | None,
|
|
91
|
+
) -> int:
|
|
92
|
+
"""Persist one message. Returns the rowid."""
|
|
93
|
+
if role not in ("user", "bot"):
|
|
94
|
+
raise ValueError(f"role must be 'user' or 'bot', got {role!r}")
|
|
95
|
+
conn = _connect(omega_home)
|
|
96
|
+
try:
|
|
97
|
+
cur = conn.execute(
|
|
98
|
+
"INSERT INTO messages (topic_id, role, text, message_id, timestamp) "
|
|
99
|
+
"VALUES (?, ?, ?, ?, ?)",
|
|
100
|
+
(int(topic_id), role, text, message_id, int(time.time())),
|
|
101
|
+
)
|
|
102
|
+
conn.commit()
|
|
103
|
+
return int(cur.lastrowid)
|
|
104
|
+
finally:
|
|
105
|
+
conn.close()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def record_inbound(
|
|
109
|
+
omega_home: str | Path,
|
|
110
|
+
topic_id: int,
|
|
111
|
+
text: str,
|
|
112
|
+
*,
|
|
113
|
+
message_id: int | None = None,
|
|
114
|
+
) -> int:
|
|
115
|
+
"""The user just sent this in the topic. Persist it."""
|
|
116
|
+
return _record(omega_home, topic_id=topic_id, role="user",
|
|
117
|
+
text=text, message_id=message_id)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def record_outbound(
|
|
121
|
+
omega_home: str | Path,
|
|
122
|
+
topic_id: int,
|
|
123
|
+
text: str,
|
|
124
|
+
*,
|
|
125
|
+
message_id: int | None = None,
|
|
126
|
+
) -> int:
|
|
127
|
+
"""The bot just posted this. Persist it so the next turn has context."""
|
|
128
|
+
return _record(omega_home, topic_id=topic_id, role="bot",
|
|
129
|
+
text=text, message_id=message_id)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def recent_messages(
|
|
133
|
+
omega_home: str | Path,
|
|
134
|
+
topic_id: int,
|
|
135
|
+
*,
|
|
136
|
+
limit: int = 20,
|
|
137
|
+
) -> list[Message]:
|
|
138
|
+
"""Return the last ``limit`` messages for this topic, oldest first.
|
|
139
|
+
|
|
140
|
+
The query selects by timestamp DESC then reverses — gives us
|
|
141
|
+
chronological order with a hard cap.
|
|
142
|
+
"""
|
|
143
|
+
conn = _connect(omega_home)
|
|
144
|
+
try:
|
|
145
|
+
rows = conn.execute(
|
|
146
|
+
"SELECT * FROM messages WHERE topic_id = ? "
|
|
147
|
+
"ORDER BY timestamp DESC, id DESC LIMIT ?",
|
|
148
|
+
(int(topic_id), int(limit)),
|
|
149
|
+
).fetchall()
|
|
150
|
+
finally:
|
|
151
|
+
conn.close()
|
|
152
|
+
return [
|
|
153
|
+
Message(
|
|
154
|
+
id=int(r["id"]),
|
|
155
|
+
topic_id=int(r["topic_id"]),
|
|
156
|
+
role=str(r["role"]),
|
|
157
|
+
text=str(r["text"]),
|
|
158
|
+
message_id=(int(r["message_id"])
|
|
159
|
+
if r["message_id"] is not None else None),
|
|
160
|
+
timestamp=int(r["timestamp"]),
|
|
161
|
+
)
|
|
162
|
+
for r in reversed(rows)
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def build_context_prompt(
|
|
167
|
+
omega_home: str | Path,
|
|
168
|
+
topic_id: int,
|
|
169
|
+
new_intent: str,
|
|
170
|
+
*,
|
|
171
|
+
limit: int = 10,
|
|
172
|
+
) -> str:
|
|
173
|
+
"""Compose a context-aware intent for ``run_mission``.
|
|
174
|
+
|
|
175
|
+
Format::
|
|
176
|
+
|
|
177
|
+
[Conversation history in topic <id>]
|
|
178
|
+
user (10 min ago): previous message
|
|
179
|
+
bot (8 min ago): previous reply
|
|
180
|
+
user (just now): ${new_intent}
|
|
181
|
+
|
|
182
|
+
Respond to the user's latest message — the messages above are
|
|
183
|
+
the conversation so far in this topic; keep your answer
|
|
184
|
+
consistent with them.
|
|
185
|
+
|
|
186
|
+
If there's no prior history, returns ``new_intent`` unchanged so a
|
|
187
|
+
cold conversation behaves as before.
|
|
188
|
+
"""
|
|
189
|
+
history = recent_messages(omega_home, topic_id, limit=limit)
|
|
190
|
+
if not history:
|
|
191
|
+
return new_intent
|
|
192
|
+
|
|
193
|
+
now = int(time.time())
|
|
194
|
+
lines: list[str] = [
|
|
195
|
+
f"[Conversation history in topic {topic_id}]"
|
|
196
|
+
]
|
|
197
|
+
for msg in history:
|
|
198
|
+
delta = now - msg.timestamp
|
|
199
|
+
when = _humanise_age(delta)
|
|
200
|
+
lines.append(f" {msg.role} ({when}): {msg.text}")
|
|
201
|
+
|
|
202
|
+
lines.append("")
|
|
203
|
+
lines.append(f"user (just now): {new_intent}")
|
|
204
|
+
lines.append("")
|
|
205
|
+
lines.append(
|
|
206
|
+
"Respond to the user's latest message — the messages above are "
|
|
207
|
+
"the conversation so far in this topic; keep your answer "
|
|
208
|
+
"consistent with them."
|
|
209
|
+
)
|
|
210
|
+
return "\n".join(lines)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _humanise_age(seconds: int) -> str:
|
|
214
|
+
"""Format an age in seconds as a short human string."""
|
|
215
|
+
if seconds < 60:
|
|
216
|
+
return f"{seconds}s ago"
|
|
217
|
+
if seconds < 3600:
|
|
218
|
+
return f"{seconds // 60}m ago"
|
|
219
|
+
if seconds < 86400:
|
|
220
|
+
return f"{seconds // 3600}h ago"
|
|
221
|
+
return f"{seconds // 86400}d ago"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def prune(
|
|
225
|
+
omega_home: str | Path,
|
|
226
|
+
*,
|
|
227
|
+
older_than_days: int = 90,
|
|
228
|
+
) -> int:
|
|
229
|
+
"""Drop messages older than ``older_than_days``. Returns rows removed."""
|
|
230
|
+
cutoff = int(time.time()) - older_than_days * 86400
|
|
231
|
+
conn = _connect(omega_home)
|
|
232
|
+
try:
|
|
233
|
+
cur = conn.execute(
|
|
234
|
+
"DELETE FROM messages WHERE timestamp < ?", (cutoff,)
|
|
235
|
+
)
|
|
236
|
+
conn.commit()
|
|
237
|
+
return int(cur.rowcount)
|
|
238
|
+
finally:
|
|
239
|
+
conn.close()
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def topic_stats(omega_home: str | Path) -> list[dict]:
|
|
243
|
+
"""For the doctor: a one-row-per-topic summary."""
|
|
244
|
+
conn = _connect(omega_home)
|
|
245
|
+
try:
|
|
246
|
+
rows = conn.execute(
|
|
247
|
+
"SELECT topic_id, COUNT(*) AS n_msgs, "
|
|
248
|
+
" MAX(timestamp) AS last_seen "
|
|
249
|
+
"FROM messages GROUP BY topic_id ORDER BY last_seen DESC"
|
|
250
|
+
).fetchall()
|
|
251
|
+
finally:
|
|
252
|
+
conn.close()
|
|
253
|
+
return [
|
|
254
|
+
{
|
|
255
|
+
"topic_id": int(r["topic_id"]),
|
|
256
|
+
"messages": int(r["n_msgs"]),
|
|
257
|
+
"last_seen": int(r["last_seen"]),
|
|
258
|
+
}
|
|
259
|
+
for r in rows
|
|
260
|
+
]
|