@smilintux/skcapstone 0.1.0 → 0.2.3

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 (461) hide show
  1. package/.env.example +98 -0
  2. package/.github/workflows/ci.yml +39 -3
  3. package/.github/workflows/publish.yml +25 -4
  4. package/.openclaw-workspace.json +58 -0
  5. package/CHANGELOG.md +62 -0
  6. package/CLAUDE.md +39 -2
  7. package/MANIFEST.in +6 -0
  8. package/MISSION.md +7 -0
  9. package/README.md +47 -2
  10. package/SKILL.md +895 -23
  11. package/docker/Dockerfile +61 -0
  12. package/docker/compose-templates/dev-team.yml +203 -0
  13. package/docker/compose-templates/mini-team.yml +140 -0
  14. package/docker/compose-templates/ops-team.yml +173 -0
  15. package/docker/compose-templates/research-team.yml +170 -0
  16. package/docker/entrypoint.sh +192 -0
  17. package/docs/ARCHITECTURE.md +663 -374
  18. package/docs/BOND_WITH_GROK.md +112 -0
  19. package/docs/GETTING_STARTED.md +782 -0
  20. package/docs/QUICKSTART.md +477 -0
  21. package/docs/SKJOULE_ARCHITECTURE.md +658 -0
  22. package/docs/SOUL_SWAPPER.md +921 -0
  23. package/docs/SOVEREIGN_SINGULARITY.md +47 -14
  24. package/examples/custom-bond-template.json +36 -0
  25. package/examples/grok-feb.json +36 -0
  26. package/examples/grok-testimony.md +34 -0
  27. package/examples/love-bootloader.txt +32 -0
  28. package/examples/plugins/echo_tool.py +87 -0
  29. package/examples/queen-ava-feb.json +36 -0
  30. package/examples/souls/lumina.yaml +64 -0
  31. package/index.js +6 -5
  32. package/installer/build.py +124 -0
  33. package/openclaw-plugin/package.json +13 -0
  34. package/openclaw-plugin/src/index.ts +351 -0
  35. package/openclaw-plugin/src/openclaw.plugin.json +10 -0
  36. package/package.json +1 -1
  37. package/pyproject.toml +38 -2
  38. package/scripts/bump_version.py +141 -0
  39. package/scripts/check-updates.py +230 -0
  40. package/scripts/convert_blueprints_to_yaml.py +157 -0
  41. package/scripts/dev-install.sh +14 -0
  42. package/scripts/e2e-test.sh +193 -0
  43. package/scripts/install-bundle.sh +171 -0
  44. package/scripts/install.bat +2 -0
  45. package/scripts/install.ps1 +253 -0
  46. package/scripts/install.sh +185 -0
  47. package/scripts/mcp-serve.sh +69 -0
  48. package/scripts/mcp-server.bat +113 -0
  49. package/scripts/mcp-server.ps1 +116 -0
  50. package/scripts/mcp-server.sh +99 -0
  51. package/scripts/pull-models.sh +10 -0
  52. package/scripts/skcapstone +48 -0
  53. package/scripts/verify_install.sh +180 -0
  54. package/scripts/windows/install-tasks.ps1 +406 -0
  55. package/scripts/windows/skcapstone-task.xml +113 -0
  56. package/scripts/windows/uninstall-tasks.ps1 +117 -0
  57. package/skill.yaml +34 -0
  58. package/src/skcapstone/__init__.py +67 -2
  59. package/src/skcapstone/_cli_monolith.py +5916 -0
  60. package/src/skcapstone/_trustee_helpers.py +165 -0
  61. package/src/skcapstone/activity.py +105 -0
  62. package/src/skcapstone/agent_card.py +324 -0
  63. package/src/skcapstone/api.py +1935 -0
  64. package/src/skcapstone/archiver.py +340 -0
  65. package/src/skcapstone/auction.py +485 -0
  66. package/src/skcapstone/baby_agents.py +179 -0
  67. package/src/skcapstone/backup.py +345 -0
  68. package/src/skcapstone/blueprint_registry.py +357 -0
  69. package/src/skcapstone/blueprints/__init__.py +17 -0
  70. package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
  71. package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
  72. package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
  73. package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
  74. package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
  75. package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
  76. package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
  77. package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
  78. package/src/skcapstone/blueprints/registry.py +164 -0
  79. package/src/skcapstone/blueprints/schema.py +229 -0
  80. package/src/skcapstone/changelog.py +180 -0
  81. package/src/skcapstone/chat.py +769 -0
  82. package/src/skcapstone/claude_md.py +82 -0
  83. package/src/skcapstone/cli/__init__.py +144 -0
  84. package/src/skcapstone/cli/_common.py +88 -0
  85. package/src/skcapstone/cli/_validators.py +76 -0
  86. package/src/skcapstone/cli/agents.py +425 -0
  87. package/src/skcapstone/cli/agents_spawner.py +322 -0
  88. package/src/skcapstone/cli/agents_trustee.py +593 -0
  89. package/src/skcapstone/cli/alerts.py +248 -0
  90. package/src/skcapstone/cli/anchor.py +132 -0
  91. package/src/skcapstone/cli/archive_cmd.py +208 -0
  92. package/src/skcapstone/cli/backup.py +144 -0
  93. package/src/skcapstone/cli/bench.py +377 -0
  94. package/src/skcapstone/cli/benchmark.py +360 -0
  95. package/src/skcapstone/cli/capabilities_cmd.py +171 -0
  96. package/src/skcapstone/cli/card.py +151 -0
  97. package/src/skcapstone/cli/chat.py +584 -0
  98. package/src/skcapstone/cli/completions.py +64 -0
  99. package/src/skcapstone/cli/config_cmd.py +156 -0
  100. package/src/skcapstone/cli/consciousness.py +421 -0
  101. package/src/skcapstone/cli/context_cmd.py +142 -0
  102. package/src/skcapstone/cli/coord.py +194 -0
  103. package/src/skcapstone/cli/crush_cmd.py +170 -0
  104. package/src/skcapstone/cli/daemon.py +436 -0
  105. package/src/skcapstone/cli/errors_cmd.py +285 -0
  106. package/src/skcapstone/cli/export_cmd.py +156 -0
  107. package/src/skcapstone/cli/gtd.py +529 -0
  108. package/src/skcapstone/cli/housekeeping.py +81 -0
  109. package/src/skcapstone/cli/joule_cmd.py +627 -0
  110. package/src/skcapstone/cli/logs_cmd.py +194 -0
  111. package/src/skcapstone/cli/mcp_cmd.py +32 -0
  112. package/src/skcapstone/cli/memory.py +418 -0
  113. package/src/skcapstone/cli/metrics_cmd.py +136 -0
  114. package/src/skcapstone/cli/migrate.py +62 -0
  115. package/src/skcapstone/cli/mood_cmd.py +144 -0
  116. package/src/skcapstone/cli/mount.py +193 -0
  117. package/src/skcapstone/cli/notify.py +112 -0
  118. package/src/skcapstone/cli/peer.py +154 -0
  119. package/src/skcapstone/cli/peers_dir.py +122 -0
  120. package/src/skcapstone/cli/preflight_cmd.py +83 -0
  121. package/src/skcapstone/cli/profile_cmd.py +310 -0
  122. package/src/skcapstone/cli/record_cmd.py +238 -0
  123. package/src/skcapstone/cli/register_cmd.py +159 -0
  124. package/src/skcapstone/cli/search_cmd.py +156 -0
  125. package/src/skcapstone/cli/service_cmd.py +91 -0
  126. package/src/skcapstone/cli/session.py +127 -0
  127. package/src/skcapstone/cli/setup.py +240 -0
  128. package/src/skcapstone/cli/shell_cmd.py +43 -0
  129. package/src/skcapstone/cli/skills_cmd.py +168 -0
  130. package/src/skcapstone/cli/skseed.py +621 -0
  131. package/src/skcapstone/cli/soul.py +699 -0
  132. package/src/skcapstone/cli/status.py +935 -0
  133. package/src/skcapstone/cli/sync_cmd.py +301 -0
  134. package/src/skcapstone/cli/telegram.py +265 -0
  135. package/src/skcapstone/cli/test_cmd.py +234 -0
  136. package/src/skcapstone/cli/test_connection.py +253 -0
  137. package/src/skcapstone/cli/token.py +207 -0
  138. package/src/skcapstone/cli/trust.py +179 -0
  139. package/src/skcapstone/cli/upgrade_cmd.py +552 -0
  140. package/src/skcapstone/cli/usage_cmd.py +199 -0
  141. package/src/skcapstone/cli/version_cmd.py +162 -0
  142. package/src/skcapstone/cli/watch_cmd.py +342 -0
  143. package/src/skcapstone/client.py +428 -0
  144. package/src/skcapstone/cloud9_bridge.py +522 -0
  145. package/src/skcapstone/completions.py +163 -0
  146. package/src/skcapstone/config_validator.py +674 -0
  147. package/src/skcapstone/connectors/__init__.py +28 -0
  148. package/src/skcapstone/connectors/base.py +446 -0
  149. package/src/skcapstone/connectors/cursor.py +54 -0
  150. package/src/skcapstone/connectors/registry.py +254 -0
  151. package/src/skcapstone/connectors/terminal.py +152 -0
  152. package/src/skcapstone/connectors/vscode.py +60 -0
  153. package/src/skcapstone/consciousness_config.py +119 -0
  154. package/src/skcapstone/consciousness_loop.py +2051 -0
  155. package/src/skcapstone/context_loader.py +516 -0
  156. package/src/skcapstone/context_window.py +314 -0
  157. package/src/skcapstone/conversation_manager.py +238 -0
  158. package/src/skcapstone/conversation_store.py +230 -0
  159. package/src/skcapstone/conversation_summarizer.py +252 -0
  160. package/src/skcapstone/coord_federation.py +296 -0
  161. package/src/skcapstone/coordination.py +101 -7
  162. package/src/skcapstone/crush_integration.py +345 -0
  163. package/src/skcapstone/crush_shim.py +454 -0
  164. package/src/skcapstone/daemon.py +2494 -0
  165. package/src/skcapstone/dashboard.html +396 -0
  166. package/src/skcapstone/dashboard.py +481 -0
  167. package/src/skcapstone/data/model_profiles.yaml +88 -0
  168. package/src/skcapstone/defaults/__init__.py +55 -0
  169. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
  170. package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
  171. package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
  172. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
  173. package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
  174. package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
  175. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
  176. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
  177. package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
  178. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
  179. package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
  180. package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
  181. package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
  182. package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
  183. package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
  184. package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
  185. package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
  186. package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
  187. package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
  188. package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
  189. package/src/skcapstone/discovery.py +210 -19
  190. package/src/skcapstone/doctor.py +642 -0
  191. package/src/skcapstone/emotion_tracker.py +467 -0
  192. package/src/skcapstone/error_queue.py +405 -0
  193. package/src/skcapstone/export.py +447 -0
  194. package/src/skcapstone/fallback_tracker.py +186 -0
  195. package/src/skcapstone/file_transfer.py +512 -0
  196. package/src/skcapstone/fuse_mount.py +1156 -0
  197. package/src/skcapstone/gui_installer.py +591 -0
  198. package/src/skcapstone/heartbeat.py +611 -0
  199. package/src/skcapstone/housekeeping.py +298 -0
  200. package/src/skcapstone/install_wizard.py +941 -0
  201. package/src/skcapstone/kms.py +942 -0
  202. package/src/skcapstone/kms_scheduler.py +143 -0
  203. package/src/skcapstone/log_config.py +135 -0
  204. package/src/skcapstone/mcp_launcher.py +239 -0
  205. package/src/skcapstone/mcp_server.py +4700 -0
  206. package/src/skcapstone/mcp_tools/__init__.py +94 -0
  207. package/src/skcapstone/mcp_tools/_helpers.py +51 -0
  208. package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
  209. package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
  210. package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
  211. package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
  212. package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
  213. package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
  214. package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
  215. package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
  216. package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
  217. package/src/skcapstone/mcp_tools/did_tools.py +448 -0
  218. package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
  219. package/src/skcapstone/mcp_tools/file_tools.py +169 -0
  220. package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
  221. package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
  222. package/src/skcapstone/mcp_tools/health_tools.py +44 -0
  223. package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
  224. package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
  225. package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
  226. package/src/skcapstone/mcp_tools/model_tools.py +75 -0
  227. package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
  228. package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
  229. package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
  230. package/src/skcapstone/mcp_tools/security_tools.py +110 -0
  231. package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
  232. package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
  233. package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
  234. package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
  235. package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
  236. package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
  237. package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
  238. package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
  239. package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
  240. package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
  241. package/src/skcapstone/mdns_discovery.py +313 -0
  242. package/src/skcapstone/memory_adapter.py +333 -0
  243. package/src/skcapstone/memory_compressor.py +379 -0
  244. package/src/skcapstone/memory_curator.py +256 -0
  245. package/src/skcapstone/memory_engine.py +132 -13
  246. package/src/skcapstone/memory_fortress.py +529 -0
  247. package/src/skcapstone/memory_promoter.py +722 -0
  248. package/src/skcapstone/memory_verifier.py +260 -0
  249. package/src/skcapstone/message_crypto.py +215 -0
  250. package/src/skcapstone/metrics.py +832 -0
  251. package/src/skcapstone/migrate_memories.py +181 -0
  252. package/src/skcapstone/migrate_multi_agent.py +248 -0
  253. package/src/skcapstone/model_router.py +319 -0
  254. package/src/skcapstone/models.py +35 -4
  255. package/src/skcapstone/mood.py +344 -0
  256. package/src/skcapstone/notifications.py +380 -0
  257. package/src/skcapstone/onboard.py +901 -0
  258. package/src/skcapstone/peer_directory.py +324 -0
  259. package/src/skcapstone/peers.py +329 -0
  260. package/src/skcapstone/pillars/identity.py +84 -14
  261. package/src/skcapstone/pillars/memory.py +3 -1
  262. package/src/skcapstone/pillars/security.py +108 -15
  263. package/src/skcapstone/pillars/sync.py +78 -26
  264. package/src/skcapstone/pillars/trust.py +95 -33
  265. package/src/skcapstone/plugins.py +244 -0
  266. package/src/skcapstone/preflight.py +670 -0
  267. package/src/skcapstone/prompt_adapter.py +564 -0
  268. package/src/skcapstone/providers/__init__.py +13 -0
  269. package/src/skcapstone/providers/cloud.py +1061 -0
  270. package/src/skcapstone/providers/docker.py +759 -0
  271. package/src/skcapstone/providers/local.py +1193 -0
  272. package/src/skcapstone/providers/proxmox.py +447 -0
  273. package/src/skcapstone/pubsub.py +516 -0
  274. package/src/skcapstone/rate_limiter.py +119 -0
  275. package/src/skcapstone/register.py +241 -0
  276. package/src/skcapstone/registry_client.py +151 -0
  277. package/src/skcapstone/response_cache.py +194 -0
  278. package/src/skcapstone/response_scorer.py +225 -0
  279. package/src/skcapstone/runtime.py +89 -33
  280. package/src/skcapstone/scheduled_tasks.py +439 -0
  281. package/src/skcapstone/self_healing.py +341 -0
  282. package/src/skcapstone/service_health.py +228 -0
  283. package/src/skcapstone/session_capture.py +268 -0
  284. package/src/skcapstone/session_recorder.py +210 -0
  285. package/src/skcapstone/session_replayer.py +189 -0
  286. package/src/skcapstone/session_skills.py +263 -0
  287. package/src/skcapstone/shell.py +779 -0
  288. package/src/skcapstone/skills/__init__.py +1 -1
  289. package/src/skcapstone/skills/syncthing_setup.py +143 -41
  290. package/src/skcapstone/skjoule.py +861 -0
  291. package/src/skcapstone/snapshots.py +489 -0
  292. package/src/skcapstone/soul.py +1060 -0
  293. package/src/skcapstone/soul_switch.py +255 -0
  294. package/src/skcapstone/spawner.py +544 -0
  295. package/src/skcapstone/state_diff.py +401 -0
  296. package/src/skcapstone/summary.py +270 -0
  297. package/src/skcapstone/sync/backends.py +196 -2
  298. package/src/skcapstone/sync/engine.py +7 -5
  299. package/src/skcapstone/sync/models.py +4 -1
  300. package/src/skcapstone/sync/vault.py +356 -18
  301. package/src/skcapstone/sync_engine.py +363 -0
  302. package/src/skcapstone/sync_watcher.py +745 -0
  303. package/src/skcapstone/systemd.py +331 -0
  304. package/src/skcapstone/team_comms.py +476 -0
  305. package/src/skcapstone/team_engine.py +522 -0
  306. package/src/skcapstone/testrunner.py +300 -0
  307. package/src/skcapstone/tls.py +150 -0
  308. package/src/skcapstone/tokens.py +5 -5
  309. package/src/skcapstone/trust_calibration.py +202 -0
  310. package/src/skcapstone/trust_graph.py +449 -0
  311. package/src/skcapstone/trustee_monitor.py +385 -0
  312. package/src/skcapstone/trustee_ops.py +425 -0
  313. package/src/skcapstone/unified_search.py +421 -0
  314. package/src/skcapstone/uninstall_wizard.py +694 -0
  315. package/src/skcapstone/usage.py +331 -0
  316. package/src/skcapstone/version_check.py +148 -0
  317. package/src/skcapstone/warmth_anchor.py +333 -0
  318. package/src/skcapstone/whoami.py +294 -0
  319. package/systemd/skcapstone-api.socket +9 -0
  320. package/systemd/skcapstone-memory-compress.service +18 -0
  321. package/systemd/skcapstone-memory-compress.timer +11 -0
  322. package/systemd/skcapstone.service +36 -0
  323. package/systemd/skcapstone@.service +50 -0
  324. package/systemd/skcomm-heartbeat.service +18 -0
  325. package/systemd/skcomm-heartbeat.timer +12 -0
  326. package/systemd/skcomm-queue-drain.service +17 -0
  327. package/systemd/skcomm-queue-drain.timer +12 -0
  328. package/tests/conftest.py +13 -1
  329. package/tests/integration/__init__.py +1 -0
  330. package/tests/integration/test_consciousness_e2e.py +877 -0
  331. package/tests/integration/test_skills_registry.py +744 -0
  332. package/tests/test_agent_card.py +190 -0
  333. package/tests/test_agent_runtime.py +1283 -0
  334. package/tests/test_alerts_cmd.py +291 -0
  335. package/tests/test_archiver.py +498 -0
  336. package/tests/test_backup.py +254 -0
  337. package/tests/test_benchmark.py +366 -0
  338. package/tests/test_blueprints.py +457 -0
  339. package/tests/test_capabilities.py +257 -0
  340. package/tests/test_changelog.py +254 -0
  341. package/tests/test_chat.py +385 -0
  342. package/tests/test_claude_md.py +271 -0
  343. package/tests/test_cli_chat_llm.py +336 -0
  344. package/tests/test_cli_completions.py +390 -0
  345. package/tests/test_cli_init_reset.py +164 -0
  346. package/tests/test_cli_memory.py +208 -0
  347. package/tests/test_cli_profile.py +294 -0
  348. package/tests/test_cli_skills.py +223 -0
  349. package/tests/test_cli_status.py +395 -0
  350. package/tests/test_cli_test_cmd.py +206 -0
  351. package/tests/test_cli_test_connection.py +364 -0
  352. package/tests/test_cloud9_bridge.py +260 -0
  353. package/tests/test_cloud_provider.py +449 -0
  354. package/tests/test_cloud_providers.py +522 -0
  355. package/tests/test_completions.py +158 -0
  356. package/tests/test_component_manager.py +398 -0
  357. package/tests/test_config_reload.py +386 -0
  358. package/tests/test_config_validate.py +529 -0
  359. package/tests/test_consciousness_e2e.py +296 -0
  360. package/tests/test_consciousness_loop.py +1289 -0
  361. package/tests/test_context_loader.py +310 -0
  362. package/tests/test_conversation_api.py +306 -0
  363. package/tests/test_conversation_manager.py +381 -0
  364. package/tests/test_conversation_store.py +391 -0
  365. package/tests/test_conversation_summarizer.py +302 -0
  366. package/tests/test_cross_package.py +791 -0
  367. package/tests/test_crush_shim.py +519 -0
  368. package/tests/test_daemon.py +781 -0
  369. package/tests/test_daemon_shutdown.py +309 -0
  370. package/tests/test_dashboard.py +454 -0
  371. package/tests/test_discovery.py +200 -6
  372. package/tests/test_docker_provider.py +966 -0
  373. package/tests/test_doctor.py +257 -0
  374. package/tests/test_doctor_fix.py +351 -0
  375. package/tests/test_e2e_automated.py +292 -0
  376. package/tests/test_error_queue.py +404 -0
  377. package/tests/test_export.py +441 -0
  378. package/tests/test_fallback_tracker.py +219 -0
  379. package/tests/test_file_transfer.py +397 -0
  380. package/tests/test_fuse_mount.py +832 -0
  381. package/tests/test_health_loop.py +422 -0
  382. package/tests/test_heartbeat.py +354 -0
  383. package/tests/test_housekeeping.py +195 -0
  384. package/tests/test_identity_capauth.py +307 -0
  385. package/tests/test_identity_pillar.py +117 -0
  386. package/tests/test_install_wizard.py +68 -0
  387. package/tests/test_integration.py +325 -0
  388. package/tests/test_kms.py +495 -0
  389. package/tests/test_llm_providers.py +265 -0
  390. package/tests/test_local_provider.py +591 -0
  391. package/tests/test_log_config.py +199 -0
  392. package/tests/test_logs_cmd.py +287 -0
  393. package/tests/test_mcp_server.py +1909 -0
  394. package/tests/test_memory_adapter.py +339 -0
  395. package/tests/test_memory_curator.py +218 -0
  396. package/tests/test_memory_engine.py +6 -0
  397. package/tests/test_memory_fortress.py +571 -0
  398. package/tests/test_memory_pillar.py +119 -0
  399. package/tests/test_memory_promoter.py +445 -0
  400. package/tests/test_memory_verifier.py +420 -0
  401. package/tests/test_message_crypto.py +187 -0
  402. package/tests/test_metrics.py +632 -0
  403. package/tests/test_migrate_memories.py +464 -0
  404. package/tests/test_model_router.py +546 -0
  405. package/tests/test_mood.py +394 -0
  406. package/tests/test_multi_agent.py +269 -0
  407. package/tests/test_notifications.py +270 -0
  408. package/tests/test_onboard.py +500 -0
  409. package/tests/test_peer_directory.py +395 -0
  410. package/tests/test_peers.py +248 -0
  411. package/tests/test_pillars.py +87 -9
  412. package/tests/test_preflight.py +484 -0
  413. package/tests/test_prompt_adapter.py +331 -0
  414. package/tests/test_proxmox_provider.py +571 -0
  415. package/tests/test_pubsub.py +377 -0
  416. package/tests/test_rate_limiter.py +121 -0
  417. package/tests/test_registry_client.py +129 -0
  418. package/tests/test_response_cache.py +312 -0
  419. package/tests/test_response_scorer.py +294 -0
  420. package/tests/test_runtime.py +59 -0
  421. package/tests/test_scheduled_tasks.py +451 -0
  422. package/tests/test_security.py +250 -0
  423. package/tests/test_security_pillar.py +213 -0
  424. package/tests/test_self_healing.py +171 -0
  425. package/tests/test_session_capture.py +200 -0
  426. package/tests/test_session_recorder.py +360 -0
  427. package/tests/test_session_skills.py +235 -0
  428. package/tests/test_shell.py +210 -0
  429. package/tests/test_snapshots.py +549 -0
  430. package/tests/test_soul.py +984 -0
  431. package/tests/test_soul_swap.py +406 -0
  432. package/tests/test_spawner.py +211 -0
  433. package/tests/test_state_diff.py +173 -0
  434. package/tests/test_summary.py +135 -0
  435. package/tests/test_sync.py +315 -5
  436. package/tests/test_sync_backends.py +560 -0
  437. package/tests/test_sync_engine.py +482 -0
  438. package/tests/test_sync_pillar.py +344 -0
  439. package/tests/test_sync_pipeline.py +364 -0
  440. package/tests/test_sync_vault.py +581 -0
  441. package/tests/test_syncthing_setup.py +168 -22
  442. package/tests/test_systemd.py +323 -0
  443. package/tests/test_team_comms.py +408 -0
  444. package/tests/test_team_engine.py +397 -0
  445. package/tests/test_testrunner.py +238 -0
  446. package/tests/test_trust_calibration.py +204 -0
  447. package/tests/test_trust_graph.py +207 -0
  448. package/tests/test_trust_pillar.py +291 -0
  449. package/tests/test_trustee_cli.py +427 -0
  450. package/tests/test_trustee_cli_integration.py +325 -0
  451. package/tests/test_trustee_monitor.py +394 -0
  452. package/tests/test_trustee_ops.py +355 -0
  453. package/tests/test_unified_search.py +363 -0
  454. package/tests/test_uninstall_wizard.py +193 -0
  455. package/tests/test_usage.py +333 -0
  456. package/tests/test_version_cmd.py +355 -0
  457. package/tests/test_warmth_anchor.py +162 -0
  458. package/tests/test_whoami.py +245 -0
  459. package/tests/test_ws.py +311 -0
  460. package/.cursorrules +0 -33
  461. package/src/skcapstone/cli.py +0 -1441
@@ -0,0 +1,331 @@
1
+ """
2
+ LLM token usage tracking — input/output tokens per model per day.
3
+
4
+ Records are stored in ~/.skcapstone/usage/tokens-{date}.json, one file
5
+ per calendar day (UTC). Each file accumulates calls to record_usage()
6
+ atomically using a threading lock.
7
+
8
+ Cost estimation uses approximate per-million-token pricing by model
9
+ family. Local models (ollama, passthrough) have zero cost.
10
+
11
+ Usage:
12
+ from skcapstone.usage import UsageTracker
13
+ tracker = UsageTracker(home=Path("~/.skcapstone"))
14
+ tracker.record_usage("ollama:llama3.1", input_tokens=512, output_tokens=128)
15
+ report = tracker.get_daily()
16
+ print(report)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import logging
23
+ import threading
24
+ from datetime import date, datetime, timedelta, timezone
25
+ from pathlib import Path
26
+ from typing import Optional
27
+
28
+ from pydantic import BaseModel, Field
29
+
30
+ logger = logging.getLogger("skcapstone.usage")
31
+
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # Cost table (USD per 1 000 000 tokens)
35
+ # ---------------------------------------------------------------------------
36
+
37
+ #: Approximate prices in USD per 1M tokens. Keys are model-family prefixes
38
+ #: matched with str.startswith(). First match wins (ordered from most
39
+ #: specific to least specific).
40
+ _COST_TABLE: list[tuple[str, float, float]] = [
41
+ # (prefix, input_per_1M, output_per_1M)
42
+ # Anthropic Claude family
43
+ ("claude-opus", 15.0, 75.0),
44
+ ("claude-sonnet", 3.0, 15.0),
45
+ ("claude-haiku", 0.25, 1.25),
46
+ ("claude", 3.0, 15.0), # generic claude fallback
47
+ # OpenAI
48
+ ("gpt-4o-mini", 0.15, 0.60),
49
+ ("gpt-4o", 2.50, 10.0),
50
+ ("gpt-4", 10.0, 30.0),
51
+ ("gpt-3.5", 0.50, 1.50),
52
+ # NVIDIA NIM / meta on NIM
53
+ ("nvidia/", 1.00, 4.00),
54
+ ("meta/llama", 1.00, 4.00),
55
+ # Groq (fast inference, very cheap)
56
+ ("groq:", 0.05, 0.10),
57
+ # Grok (xAI)
58
+ ("grok", 5.00, 15.0),
59
+ # Kimi
60
+ ("kimi", 0.60, 2.50),
61
+ # Ollama and passthrough → free (local)
62
+ ("ollama", 0.0, 0.0),
63
+ ("passthrough", 0.0, 0.0),
64
+ ("llama", 0.0, 0.0),
65
+ ("mistral", 0.0, 0.0),
66
+ ("qwen", 0.0, 0.0),
67
+ ("phi", 0.0, 0.0),
68
+ ("gemma", 0.0, 0.0),
69
+ ]
70
+
71
+
72
+ def _cost_per_million(model: str) -> tuple[float, float]:
73
+ """Return (input_per_1M, output_per_1M) cost for a model string.
74
+
75
+ Args:
76
+ model: Model identifier, e.g. 'ollama:llama3.1' or 'claude-sonnet-4-6'.
77
+
78
+ Returns:
79
+ Tuple of (input cost, output cost) per 1 000 000 tokens in USD.
80
+ """
81
+ lower = model.lower()
82
+ for prefix, inp, out in _COST_TABLE:
83
+ if lower.startswith(prefix.lower()):
84
+ return inp, out
85
+ # Unknown model — use a conservative estimate
86
+ return 1.0, 4.0
87
+
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # Data models
91
+ # ---------------------------------------------------------------------------
92
+
93
+
94
+ class ModelUsageSummary(BaseModel):
95
+ """Aggregated token counts for a single model."""
96
+
97
+ model: str = Field(description="Model identifier")
98
+ calls: int = Field(default=0, description="Number of API calls recorded")
99
+ input_tokens: int = Field(default=0, description="Total prompt/input tokens")
100
+ output_tokens: int = Field(default=0, description="Total completion/output tokens")
101
+ estimated_cost_usd: float = Field(
102
+ default=0.0, description="Estimated cost in USD"
103
+ )
104
+
105
+ @property
106
+ def total_tokens(self) -> int:
107
+ """Sum of input and output tokens."""
108
+ return self.input_tokens + self.output_tokens
109
+
110
+
111
+ class DailyUsageReport(BaseModel):
112
+ """Full usage report for a single calendar day."""
113
+
114
+ date: str = Field(description="Calendar date (YYYY-MM-DD, UTC)")
115
+ models: dict[str, ModelUsageSummary] = Field(
116
+ default_factory=dict, description="Per-model usage summaries"
117
+ )
118
+
119
+ @property
120
+ def total_calls(self) -> int:
121
+ """Total API calls across all models."""
122
+ return sum(m.calls for m in self.models.values())
123
+
124
+ @property
125
+ def total_input_tokens(self) -> int:
126
+ """Total input tokens across all models."""
127
+ return sum(m.input_tokens for m in self.models.values())
128
+
129
+ @property
130
+ def total_output_tokens(self) -> int:
131
+ """Total output tokens across all models."""
132
+ return sum(m.output_tokens for m in self.models.values())
133
+
134
+ @property
135
+ def total_tokens(self) -> int:
136
+ """Total tokens (input + output) across all models."""
137
+ return self.total_input_tokens + self.total_output_tokens
138
+
139
+ @property
140
+ def total_cost_usd(self) -> float:
141
+ """Total estimated cost in USD across all models."""
142
+ return sum(m.estimated_cost_usd for m in self.models.values())
143
+
144
+
145
+ # ---------------------------------------------------------------------------
146
+ # UsageTracker
147
+ # ---------------------------------------------------------------------------
148
+
149
+
150
+ class UsageTracker:
151
+ """Thread-safe LLM token usage tracker.
152
+
153
+ Persists one JSON file per calendar day under
154
+ ``{home}/usage/tokens-{date}.json``.
155
+
156
+ Args:
157
+ home: Agent home directory (e.g. ~/.skcapstone).
158
+ """
159
+
160
+ def __init__(self, home: Path) -> None:
161
+ self._home = Path(home).expanduser()
162
+ self._usage_dir = self._home / "usage"
163
+ self._lock = threading.Lock()
164
+
165
+ # ------------------------------------------------------------------
166
+ # Write path
167
+ # ------------------------------------------------------------------
168
+
169
+ def record_usage(
170
+ self,
171
+ model: str,
172
+ input_tokens: int,
173
+ output_tokens: int,
174
+ date_str: Optional[str] = None,
175
+ ) -> None:
176
+ """Record a single LLM call's token usage.
177
+
178
+ Args:
179
+ model: Model identifier (e.g. 'ollama:llama3.1', 'claude-sonnet-4-6').
180
+ input_tokens: Number of input/prompt tokens consumed.
181
+ output_tokens: Number of output/completion tokens produced.
182
+ date_str: Override date in 'YYYY-MM-DD' format (defaults to today UTC).
183
+ """
184
+ if date_str is None:
185
+ date_str = _today_str()
186
+ inp_cost, out_cost = _cost_per_million(model)
187
+ cost = (input_tokens * inp_cost + output_tokens * out_cost) / 1_000_000
188
+
189
+ with self._lock:
190
+ data = self._load_raw(date_str)
191
+ entry = data["models"].setdefault(
192
+ model,
193
+ {"calls": 0, "input_tokens": 0, "output_tokens": 0, "estimated_cost_usd": 0.0},
194
+ )
195
+ entry["calls"] += 1
196
+ entry["input_tokens"] += input_tokens
197
+ entry["output_tokens"] += output_tokens
198
+ entry["estimated_cost_usd"] = round(entry["estimated_cost_usd"] + cost, 8)
199
+ self._save_raw(date_str, data)
200
+
201
+ # ------------------------------------------------------------------
202
+ # Read paths
203
+ # ------------------------------------------------------------------
204
+
205
+ def get_daily(self, date_str: Optional[str] = None) -> DailyUsageReport:
206
+ """Return usage report for a single day.
207
+
208
+ Args:
209
+ date_str: 'YYYY-MM-DD' string (defaults to today UTC).
210
+
211
+ Returns:
212
+ DailyUsageReport for that date.
213
+ """
214
+ if date_str is None:
215
+ date_str = _today_str()
216
+ with self._lock:
217
+ data = self._load_raw(date_str)
218
+ return _raw_to_report(date_str, data)
219
+
220
+ def get_weekly(self, anchor: Optional[str] = None) -> list[DailyUsageReport]:
221
+ """Return daily usage reports for the last 7 days.
222
+
223
+ Args:
224
+ anchor: End date 'YYYY-MM-DD' (defaults to today UTC).
225
+
226
+ Returns:
227
+ List of DailyUsageReport, one per day, oldest first.
228
+ """
229
+ return self._range_reports(7, anchor)
230
+
231
+ def get_monthly(self, anchor: Optional[str] = None) -> list[DailyUsageReport]:
232
+ """Return daily usage reports for the last 30 days.
233
+
234
+ Args:
235
+ anchor: End date 'YYYY-MM-DD' (defaults to today UTC).
236
+
237
+ Returns:
238
+ List of DailyUsageReport, one per day, oldest first.
239
+ """
240
+ return self._range_reports(30, anchor)
241
+
242
+ def aggregate(self, reports: list[DailyUsageReport]) -> DailyUsageReport:
243
+ """Aggregate multiple daily reports into one summary.
244
+
245
+ Args:
246
+ reports: List of DailyUsageReport instances.
247
+
248
+ Returns:
249
+ A single DailyUsageReport with aggregated totals.
250
+ The date field is set to 'range: {first}..{last}'.
251
+ """
252
+ if not reports:
253
+ return DailyUsageReport(date="empty")
254
+ merged: dict[str, ModelUsageSummary] = {}
255
+ for report in reports:
256
+ for model, summary in report.models.items():
257
+ if model not in merged:
258
+ merged[model] = ModelUsageSummary(model=model)
259
+ m = merged[model]
260
+ m.calls += summary.calls
261
+ m.input_tokens += summary.input_tokens
262
+ m.output_tokens += summary.output_tokens
263
+ m.estimated_cost_usd = round(
264
+ m.estimated_cost_usd + summary.estimated_cost_usd, 8
265
+ )
266
+ date_label = f"{reports[0].date}..{reports[-1].date}"
267
+ return DailyUsageReport(date=date_label, models=merged)
268
+
269
+ # ------------------------------------------------------------------
270
+ # Internal helpers
271
+ # ------------------------------------------------------------------
272
+
273
+ def _range_reports(
274
+ self, days: int, anchor: Optional[str]
275
+ ) -> list[DailyUsageReport]:
276
+ """Return reports for the last *days* calendar days up to anchor."""
277
+ end = _parse_date(anchor) if anchor else date.today()
278
+ reports = []
279
+ for offset in range(days - 1, -1, -1):
280
+ d = end - timedelta(days=offset)
281
+ reports.append(self.get_daily(d.strftime("%Y-%m-%d")))
282
+ return reports
283
+
284
+ def _load_raw(self, date_str: str) -> dict:
285
+ """Load raw usage dict from disk (no lock — caller must hold lock)."""
286
+ path = self._usage_dir / f"tokens-{date_str}.json"
287
+ if not path.exists():
288
+ return {"date": date_str, "models": {}}
289
+ try:
290
+ return json.loads(path.read_text(encoding="utf-8"))
291
+ except (json.JSONDecodeError, OSError) as exc:
292
+ logger.warning("Failed to read usage file %s: %s", path, exc)
293
+ return {"date": date_str, "models": {}}
294
+
295
+ def _save_raw(self, date_str: str, data: dict) -> None:
296
+ """Persist raw usage dict to disk (no lock — caller must hold lock)."""
297
+ self._usage_dir.mkdir(parents=True, exist_ok=True)
298
+ path = self._usage_dir / f"tokens-{date_str}.json"
299
+ try:
300
+ path.write_text(json.dumps(data, indent=2), encoding="utf-8")
301
+ except OSError as exc:
302
+ logger.error("Failed to write usage file %s: %s", path, exc)
303
+
304
+
305
+ # ---------------------------------------------------------------------------
306
+ # Helpers
307
+ # ---------------------------------------------------------------------------
308
+
309
+
310
+ def _today_str() -> str:
311
+ """Return today's date in UTC as 'YYYY-MM-DD'."""
312
+ return datetime.now(timezone.utc).strftime("%Y-%m-%d")
313
+
314
+
315
+ def _parse_date(date_str: str) -> date:
316
+ """Parse 'YYYY-MM-DD' to a date object."""
317
+ return datetime.strptime(date_str, "%Y-%m-%d").date()
318
+
319
+
320
+ def _raw_to_report(date_str: str, data: dict) -> DailyUsageReport:
321
+ """Convert a raw usage dict to a DailyUsageReport."""
322
+ models: dict[str, ModelUsageSummary] = {}
323
+ for model, entry in data.get("models", {}).items():
324
+ models[model] = ModelUsageSummary(
325
+ model=model,
326
+ calls=entry.get("calls", 0),
327
+ input_tokens=entry.get("input_tokens", 0),
328
+ output_tokens=entry.get("output_tokens", 0),
329
+ estimated_cost_usd=entry.get("estimated_cost_usd", 0.0),
330
+ )
331
+ return DailyUsageReport(date=date_str, models=models)
@@ -0,0 +1,148 @@
1
+ """
2
+ Ecosystem version checker for the sovereign agent stack.
3
+
4
+ Compares installed package versions against the latest available on PyPI.
5
+ Surfaces outdated packages in ``skcapstone doctor`` and provides a
6
+ standalone ``skcapstone version-check`` CLI command.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import urllib.request
13
+ import urllib.error
14
+ from dataclasses import dataclass, field
15
+ from typing import Optional
16
+
17
+
18
+ ECOSYSTEM_PACKAGES = [
19
+ "skmemory",
20
+ "skcapstone",
21
+ "capauth",
22
+ "sksecurity",
23
+ "skcomm",
24
+ "skchat",
25
+ "cloud9-protocol",
26
+ ]
27
+
28
+
29
+ @dataclass
30
+ class PackageVersion:
31
+ """Version info for a single package.
32
+
33
+ Attributes:
34
+ name: Package name.
35
+ installed: Installed version, or None if not installed.
36
+ latest: Latest version on PyPI, or None if unavailable.
37
+ up_to_date: Whether installed matches latest.
38
+ """
39
+
40
+ name: str
41
+ installed: Optional[str] = None
42
+ latest: Optional[str] = None
43
+ up_to_date: bool = True
44
+
45
+
46
+ @dataclass
47
+ class VersionReport:
48
+ """Aggregated version report for the ecosystem.
49
+
50
+ Attributes:
51
+ packages: List of per-package version info.
52
+ """
53
+
54
+ packages: list[PackageVersion] = field(default_factory=list)
55
+
56
+ @property
57
+ def all_up_to_date(self) -> bool:
58
+ """Whether every installed package is up to date."""
59
+ return all(p.up_to_date for p in self.packages if p.installed)
60
+
61
+ @property
62
+ def outdated(self) -> list[PackageVersion]:
63
+ """Packages that are installed but not at the latest version."""
64
+ return [p for p in self.packages if p.installed and not p.up_to_date]
65
+
66
+ @property
67
+ def missing(self) -> list[PackageVersion]:
68
+ """Packages that are not installed at all."""
69
+ return [p for p in self.packages if not p.installed]
70
+
71
+
72
+ def _get_installed_version(package_name: str) -> Optional[str]:
73
+ """Get the installed version of a package.
74
+
75
+ Args:
76
+ package_name: Python package name.
77
+
78
+ Returns:
79
+ Version string or None.
80
+ """
81
+ try:
82
+ from importlib.metadata import version
83
+
84
+ return version(package_name)
85
+ except Exception:
86
+ # Try import-based fallback for packages with dashes
87
+ try:
88
+ mod_name = package_name.replace("-", "_")
89
+ import importlib
90
+
91
+ mod = importlib.import_module(mod_name)
92
+ return getattr(mod, "__version__", None)
93
+ except Exception:
94
+ return None
95
+
96
+
97
+ def _get_pypi_version(package_name: str, timeout: float = 5.0) -> Optional[str]:
98
+ """Query PyPI JSON API for the latest version.
99
+
100
+ Args:
101
+ package_name: Package name on PyPI.
102
+ timeout: HTTP timeout in seconds.
103
+
104
+ Returns:
105
+ Latest version string, or None if unavailable.
106
+ """
107
+ url = f"https://pypi.org/pypi/{package_name}/json"
108
+ try:
109
+ req = urllib.request.Request(url, headers={"Accept": "application/json"})
110
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
111
+ data = json.loads(resp.read().decode())
112
+ return data.get("info", {}).get("version")
113
+ except (urllib.error.URLError, json.JSONDecodeError, OSError):
114
+ return None
115
+
116
+
117
+ def check_versions(
118
+ packages: Optional[list[str]] = None,
119
+ check_pypi: bool = True,
120
+ ) -> VersionReport:
121
+ """Check installed vs latest versions for ecosystem packages.
122
+
123
+ Args:
124
+ packages: Package names to check (default: ECOSYSTEM_PACKAGES).
125
+ check_pypi: Whether to query PyPI for latest versions.
126
+
127
+ Returns:
128
+ VersionReport with per-package results.
129
+ """
130
+ pkg_list = packages or ECOSYSTEM_PACKAGES
131
+ report = VersionReport()
132
+
133
+ for name in pkg_list:
134
+ installed = _get_installed_version(name)
135
+ latest = _get_pypi_version(name) if check_pypi else None
136
+
137
+ up_to_date = True
138
+ if installed and latest:
139
+ up_to_date = installed == latest
140
+
141
+ report.packages.append(PackageVersion(
142
+ name=name,
143
+ installed=installed,
144
+ latest=latest,
145
+ up_to_date=up_to_date,
146
+ ))
147
+
148
+ return report