@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,381 @@
1
+ """Tests for ConversationManager — centralized peer conversation management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+ from skcapstone.conversation_manager import ConversationManager, _sanitize_peer_name
11
+
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # Fixtures
15
+ # ---------------------------------------------------------------------------
16
+
17
+
18
+ @pytest.fixture
19
+ def home(tmp_path) -> Path:
20
+ """Minimal agent home with a conversations directory."""
21
+ h = tmp_path / ".skcapstone"
22
+ h.mkdir()
23
+ return h
24
+
25
+
26
+ @pytest.fixture
27
+ def manager(home) -> ConversationManager:
28
+ """Fresh ConversationManager on an empty home."""
29
+ return ConversationManager(home, max_history_messages=10)
30
+
31
+
32
+ @pytest.fixture
33
+ def populated_home(tmp_path) -> Path:
34
+ """Agent home pre-populated with two peers."""
35
+ h = tmp_path / ".skcapstone"
36
+ conv_dir = h / "conversations"
37
+ conv_dir.mkdir(parents=True)
38
+ alice = [
39
+ {"role": "user", "content": "Hello Alice", "timestamp": "2026-01-01T10:00:00+00:00"},
40
+ {"role": "assistant", "content": "Hi there!", "timestamp": "2026-01-01T10:00:01+00:00"},
41
+ ]
42
+ bob = [
43
+ {"role": "user", "content": "Hey Bob", "timestamp": "2026-01-02T12:00:00+00:00"},
44
+ ]
45
+ (conv_dir / "alice.json").write_text(json.dumps(alice), encoding="utf-8")
46
+ (conv_dir / "bob.json").write_text(json.dumps(bob), encoding="utf-8")
47
+ return h
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # _sanitize_peer_name unit tests
52
+ # ---------------------------------------------------------------------------
53
+
54
+
55
+ class TestSanitizePeerName:
56
+ def test_normal_name(self):
57
+ assert _sanitize_peer_name("alice") == "alice"
58
+
59
+ def test_strips_slashes(self):
60
+ result = _sanitize_peer_name("../../etc/passwd")
61
+ assert "/" not in result
62
+ assert ".." not in result
63
+
64
+ def test_strips_null_bytes(self):
65
+ assert "\x00" not in _sanitize_peer_name("evil\x00peer")
66
+
67
+ def test_empty_returns_unknown(self):
68
+ assert _sanitize_peer_name("") == "unknown"
69
+
70
+ def test_none_returns_unknown(self):
71
+ assert _sanitize_peer_name(None) == "unknown" # type: ignore[arg-type]
72
+
73
+ def test_max_length(self):
74
+ assert len(_sanitize_peer_name("a" * 100)) == 64
75
+
76
+ def test_allowed_chars(self):
77
+ assert _sanitize_peer_name("user@domain.io") == "user@domain.io"
78
+ assert _sanitize_peer_name("my-peer_01") == "my-peer_01"
79
+
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # list_peers
83
+ # ---------------------------------------------------------------------------
84
+
85
+
86
+ class TestListPeers:
87
+ def test_empty_returns_empty_list(self, manager):
88
+ assert manager.list_peers() == []
89
+
90
+ def test_shows_peer_after_message(self, manager):
91
+ manager.add_message("jarvis", "user", "Hello")
92
+ peers = manager.list_peers()
93
+ assert len(peers) == 1
94
+ assert peers[0]["peer"] == "jarvis"
95
+
96
+ def test_includes_message_count(self, manager):
97
+ manager.add_message("jarvis", "user", "msg1")
98
+ manager.add_message("jarvis", "assistant", "msg2")
99
+ peers = manager.list_peers()
100
+ assert peers[0]["message_count"] == 2
101
+
102
+ def test_includes_last_message_preview(self, manager):
103
+ manager.add_message("jarvis", "user", "first")
104
+ manager.add_message("jarvis", "assistant", "second")
105
+ peers = manager.list_peers()
106
+ assert "second" in peers[0]["last_message_preview"]
107
+
108
+ def test_includes_last_message_time(self, manager):
109
+ manager.add_message("jarvis", "user", "hello")
110
+ peers = manager.list_peers()
111
+ assert peers[0]["last_message_time"] is not None
112
+
113
+ def test_loads_from_disk_on_init(self, populated_home):
114
+ mgr = ConversationManager(populated_home)
115
+ peers = {p["peer"] for p in mgr.list_peers()}
116
+ assert "alice" in peers
117
+ assert "bob" in peers
118
+
119
+ def test_multiple_peers_all_listed(self, manager):
120
+ manager.add_message("alice", "user", "hi")
121
+ manager.add_message("bob", "user", "hey")
122
+ peers = {p["peer"] for p in manager.list_peers()}
123
+ assert peers == {"alice", "bob"}
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # get_history
128
+ # ---------------------------------------------------------------------------
129
+
130
+
131
+ class TestGetHistory:
132
+ def test_unknown_peer_returns_empty(self, manager):
133
+ assert manager.get_history("nobody") == []
134
+
135
+ def test_returns_added_messages(self, manager):
136
+ manager.add_message("jarvis", "user", "Hello")
137
+ manager.add_message("jarvis", "assistant", "Hi")
138
+ history = manager.get_history("jarvis")
139
+ assert len(history) == 2
140
+ assert history[0]["role"] == "user"
141
+ assert history[1]["role"] == "assistant"
142
+
143
+ def test_returns_copy_not_reference(self, manager):
144
+ manager.add_message("jarvis", "user", "msg")
145
+ h1 = manager.get_history("jarvis")
146
+ h1.append({"role": "fake"})
147
+ h2 = manager.get_history("jarvis")
148
+ assert len(h2) == 1 # original unaffected
149
+
150
+ def test_loads_existing_from_disk(self, populated_home):
151
+ mgr = ConversationManager(populated_home)
152
+ history = mgr.get_history("alice")
153
+ assert len(history) == 2
154
+ assert history[0]["content"] == "Hello Alice"
155
+
156
+ def test_sanitizes_peer_name(self, manager):
157
+ manager.add_message("../evil", "user", "test")
158
+ # sanitized name has no slashes
159
+ result = manager.get_history("../evil")
160
+ assert len(result) == 1
161
+
162
+
163
+ # ---------------------------------------------------------------------------
164
+ # add_message
165
+ # ---------------------------------------------------------------------------
166
+
167
+
168
+ class TestAddMessage:
169
+ def test_returns_message_dict(self, manager):
170
+ msg = manager.add_message("peer", "user", "hello")
171
+ assert msg["role"] == "user"
172
+ assert msg["content"] == "hello"
173
+ assert "timestamp" in msg
174
+
175
+ def test_persists_to_disk(self, manager, home):
176
+ manager.add_message("jarvis", "user", "Hello!")
177
+ conv_file = home / "conversations" / "jarvis.json"
178
+ assert conv_file.exists()
179
+ data = json.loads(conv_file.read_text())
180
+ assert data[0]["content"] == "Hello!"
181
+
182
+ def test_atomic_write_no_tmp_file(self, manager, home):
183
+ manager.add_message("ava", "user", "Test")
184
+ tmp = home / "conversations" / "ava.json.tmp"
185
+ assert not tmp.exists()
186
+
187
+ def test_caps_at_max_history_messages(self, home):
188
+ mgr = ConversationManager(home, max_history_messages=3)
189
+ for i in range(5):
190
+ mgr.add_message("peer", "user", f"msg {i}")
191
+ history = mgr.get_history("peer")
192
+ assert len(history) == 3
193
+ assert history[-1]["content"] == "msg 4"
194
+
195
+ def test_caps_persisted_file_too(self, home):
196
+ mgr = ConversationManager(home, max_history_messages=3)
197
+ for i in range(5):
198
+ mgr.add_message("peer", "user", f"msg {i}")
199
+ data = json.loads((home / "conversations" / "peer.json").read_text())
200
+ assert len(data) == 3
201
+
202
+ def test_multiple_peers_separate_files(self, manager, home):
203
+ manager.add_message("alice", "user", "Hello from alice")
204
+ manager.add_message("bob", "user", "Hello from bob")
205
+ assert (home / "conversations" / "alice.json").exists()
206
+ assert (home / "conversations" / "bob.json").exists()
207
+ alice_data = json.loads((home / "conversations" / "alice.json").read_text())
208
+ bob_data = json.loads((home / "conversations" / "bob.json").read_text())
209
+ assert alice_data[0]["content"] == "Hello from alice"
210
+ assert bob_data[0]["content"] == "Hello from bob"
211
+
212
+
213
+ # ---------------------------------------------------------------------------
214
+ # search
215
+ # ---------------------------------------------------------------------------
216
+
217
+
218
+ class TestSearch:
219
+ def test_finds_matching_content(self, manager):
220
+ manager.add_message("alice", "user", "Let's debug this function")
221
+ results = manager.search("debug")
222
+ assert len(results) == 1
223
+ assert results[0]["peer"] == "alice"
224
+ assert results[0]["content"] == "Let's debug this function"
225
+
226
+ def test_case_insensitive(self, manager):
227
+ manager.add_message("peer", "user", "Hello World")
228
+ assert len(manager.search("hello")) == 1
229
+ assert len(manager.search("HELLO")) == 1
230
+ assert len(manager.search("Hello")) == 1
231
+
232
+ def test_no_match_returns_empty(self, manager):
233
+ manager.add_message("peer", "user", "Something else")
234
+ assert manager.search("quantum") == []
235
+
236
+ def test_search_across_multiple_peers(self, manager):
237
+ manager.add_message("alice", "user", "memory test one")
238
+ manager.add_message("bob", "user", "memory test two")
239
+ manager.add_message("charlie", "user", "unrelated message")
240
+ results = manager.search("memory test")
241
+ peers = {r["peer"] for r in results}
242
+ assert "alice" in peers
243
+ assert "bob" in peers
244
+ assert "charlie" not in peers
245
+
246
+ def test_result_includes_expected_fields(self, manager):
247
+ manager.add_message("peer", "user", "test content")
248
+ result = manager.search("test")[0]
249
+ assert "peer" in result
250
+ assert "role" in result
251
+ assert "content" in result
252
+ assert "timestamp" in result
253
+
254
+ def test_empty_manager_returns_empty(self, manager):
255
+ assert manager.search("anything") == []
256
+
257
+
258
+ # ---------------------------------------------------------------------------
259
+ # export_all
260
+ # ---------------------------------------------------------------------------
261
+
262
+
263
+ class TestExportAll:
264
+ def test_empty_manager_returns_empty_dict(self, manager):
265
+ assert manager.export_all() == {}
266
+
267
+ def test_includes_all_peers(self, manager):
268
+ manager.add_message("alice", "user", "hi")
269
+ manager.add_message("bob", "user", "hey")
270
+ exported = manager.export_all()
271
+ assert set(exported.keys()) == {"alice", "bob"}
272
+
273
+ def test_messages_preserved(self, manager):
274
+ manager.add_message("alice", "user", "Hello")
275
+ manager.add_message("alice", "assistant", "Hi!")
276
+ exported = manager.export_all()
277
+ assert len(exported["alice"]) == 2
278
+ assert exported["alice"][0]["content"] == "Hello"
279
+
280
+ def test_returns_copy_not_reference(self, manager):
281
+ manager.add_message("peer", "user", "msg")
282
+ exported = manager.export_all()
283
+ exported["peer"].append({"role": "fake"})
284
+ assert len(manager.export_all()["peer"]) == 1
285
+
286
+ def test_loaded_from_disk_is_exported(self, populated_home):
287
+ mgr = ConversationManager(populated_home)
288
+ exported = mgr.export_all()
289
+ assert "alice" in exported
290
+ assert "bob" in exported
291
+
292
+
293
+ # ---------------------------------------------------------------------------
294
+ # delete
295
+ # ---------------------------------------------------------------------------
296
+
297
+
298
+ class TestDelete:
299
+ def test_delete_removes_from_memory(self, manager):
300
+ manager.add_message("alice", "user", "hello")
301
+ manager.delete("alice")
302
+ assert manager.get_history("alice") == []
303
+
304
+ def test_delete_removes_file(self, manager, home):
305
+ manager.add_message("alice", "user", "hello")
306
+ assert (home / "conversations" / "alice.json").exists()
307
+ manager.delete("alice")
308
+ assert not (home / "conversations" / "alice.json").exists()
309
+
310
+ def test_delete_nonexistent_returns_false(self, manager):
311
+ assert manager.delete("nobody") is False
312
+
313
+ def test_delete_does_not_affect_other_peers(self, manager, home):
314
+ manager.add_message("alice", "user", "hello")
315
+ manager.add_message("bob", "user", "hey")
316
+ manager.delete("bob")
317
+ assert manager.get_history("alice") != []
318
+ assert (home / "conversations" / "alice.json").exists()
319
+
320
+
321
+ # ---------------------------------------------------------------------------
322
+ # format_history_for_prompt
323
+ # ---------------------------------------------------------------------------
324
+
325
+
326
+ class TestFormatHistoryForPrompt:
327
+ def test_empty_returns_empty_string(self, manager):
328
+ assert manager.format_history_for_prompt("nobody") == ""
329
+
330
+ def test_includes_peer_name_header(self, manager):
331
+ manager.add_message("jarvis", "user", "Hello")
332
+ result = manager.format_history_for_prompt("jarvis")
333
+ assert "jarvis" in result
334
+
335
+ def test_includes_message_content(self, manager):
336
+ manager.add_message("jarvis", "user", "specific content here")
337
+ result = manager.format_history_for_prompt("jarvis")
338
+ assert "specific content here" in result
339
+
340
+ def test_respects_max_messages(self, manager):
341
+ for i in range(10):
342
+ manager.add_message("peer", "user", f"message {i}")
343
+ result = manager.format_history_for_prompt("peer", max_messages=3)
344
+ # Only last 3 messages should appear
345
+ assert "message 9" in result
346
+ assert "message 7" in result
347
+ assert "message 0" not in result
348
+
349
+
350
+ # ---------------------------------------------------------------------------
351
+ # Persistence round-trip
352
+ # ---------------------------------------------------------------------------
353
+
354
+
355
+ class TestPersistenceRoundTrip:
356
+ def test_new_manager_picks_up_written_conversations(self, home):
357
+ """Messages written by one ConversationManager are readable by another."""
358
+ mgr1 = ConversationManager(home)
359
+ mgr1.add_message("jarvis", "user", "Persistent hello")
360
+ mgr1.add_message("jarvis", "assistant", "Persisted response")
361
+
362
+ mgr2 = ConversationManager(home)
363
+ history = mgr2.get_history("jarvis")
364
+ assert len(history) == 2
365
+ assert history[0]["content"] == "Persistent hello"
366
+ assert history[1]["content"] == "Persisted response"
367
+
368
+ def test_cap_honoured_on_reload(self, home):
369
+ """History cap is applied when loading existing files."""
370
+ conv_dir = home / "conversations"
371
+ conv_dir.mkdir(parents=True, exist_ok=True)
372
+ big_history = [
373
+ {"role": "user", "content": f"msg {i}", "timestamp": "2026-01-01T00:00:00+00:00"}
374
+ for i in range(20)
375
+ ]
376
+ (conv_dir / "peer.json").write_text(json.dumps(big_history), encoding="utf-8")
377
+
378
+ mgr = ConversationManager(home, max_history_messages=5)
379
+ history = mgr.get_history("peer")
380
+ assert len(history) == 5
381
+ assert history[-1]["content"] == "msg 19"