@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,512 @@
1
+ """
2
+ Encrypted file transfer — chunked, resumable, sovereign.
3
+
4
+ Files are split into 256 KB chunks, each independently encrypted
5
+ using the agent's KMS-derived service key (Fernet AES-128-CBC).
6
+ Transfers can be paused and resumed by tracking which chunks have
7
+ been sent/received.
8
+
9
+ Architecture:
10
+ Sender:
11
+ 1. Split file into 256 KB chunks.
12
+ 2. Encrypt each chunk with KMS service key.
13
+ 3. Write chunk files to outbox directory.
14
+ 4. Create a transfer manifest (JSON) with metadata.
15
+
16
+ Receiver:
17
+ 1. Read manifest to learn expected chunks.
18
+ 2. Decrypt and verify each chunk (HMAC in Fernet).
19
+ 3. Reassemble in order.
20
+ 4. Verify final SHA-256 of complete file.
21
+
22
+ Storage layout:
23
+ ~/.skcapstone/file-transfer/
24
+ ├── outbox/
25
+ │ └── <transfer_id>/
26
+ │ ├── manifest.json
27
+ │ ├── chunk-000.enc
28
+ │ ├── chunk-001.enc
29
+ │ └── ...
30
+ ├── inbox/
31
+ │ └── <transfer_id>/
32
+ │ ├── manifest.json
33
+ │ └── ...
34
+ └── completed/
35
+ └── <transfer_id>.json # completion receipt
36
+
37
+ Usage:
38
+ ft = FileTransfer(home)
39
+ ft.initialize()
40
+ transfer = ft.send("/path/to/file.pdf", recipient="lumina")
41
+ # Receiver side:
42
+ assembled = ft.receive(transfer_id)
43
+ """
44
+
45
+ from __future__ import annotations
46
+
47
+ import hashlib
48
+ import json
49
+ import logging
50
+ import uuid
51
+ from datetime import datetime, timezone
52
+ from pathlib import Path
53
+ from typing import Any, Optional
54
+
55
+ from pydantic import BaseModel, Field
56
+
57
+ logger = logging.getLogger("skcapstone.file_transfer")
58
+
59
+ CHUNK_SIZE = 256 * 1024 # 256 KB
60
+
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # Models
64
+ # ---------------------------------------------------------------------------
65
+
66
+
67
+ class ChunkInfo(BaseModel):
68
+ """Metadata for a single file chunk."""
69
+
70
+ index: int
71
+ size: int
72
+ sha256: str
73
+ encrypted: bool = True
74
+ sent: bool = False
75
+ received: bool = False
76
+
77
+
78
+ class TransferManifest(BaseModel):
79
+ """Manifest describing a complete file transfer."""
80
+
81
+ transfer_id: str = Field(default_factory=lambda: uuid.uuid4().hex[:12])
82
+ filename: str
83
+ file_size: int
84
+ file_sha256: str
85
+ chunk_size: int = CHUNK_SIZE
86
+ total_chunks: int
87
+ sender: str = ""
88
+ recipient: str = ""
89
+ created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
90
+ completed_at: Optional[datetime] = None
91
+ chunks: list[ChunkInfo] = Field(default_factory=list)
92
+ encryption_key_label: str = "file-transfer"
93
+
94
+ @property
95
+ def is_complete(self) -> bool:
96
+ """Whether all chunks have been sent and received."""
97
+ return all(c.sent for c in self.chunks) and all(c.received for c in self.chunks)
98
+
99
+ @property
100
+ def progress(self) -> float:
101
+ """Transfer progress as a fraction (0.0 to 1.0)."""
102
+ if not self.chunks:
103
+ return 0.0
104
+ done = sum(1 for c in self.chunks if c.sent or c.received)
105
+ return done / len(self.chunks)
106
+
107
+
108
+ class TransferStatus(BaseModel):
109
+ """Summary of a file transfer."""
110
+
111
+ transfer_id: str
112
+ filename: str
113
+ file_size: int
114
+ direction: str # "send" or "receive"
115
+ progress: float
116
+ total_chunks: int
117
+ chunks_done: int
118
+ sender: str = ""
119
+ recipient: str = ""
120
+ created_at: Optional[datetime] = None
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # FileTransfer
125
+ # ---------------------------------------------------------------------------
126
+
127
+
128
+ class FileTransfer:
129
+ """Encrypted chunked file transfer engine.
130
+
131
+ Splits files into 256 KB chunks, encrypts each with a KMS
132
+ service key, and manages transfer state for resumability.
133
+
134
+ Args:
135
+ home: Agent home directory (~/.skcapstone).
136
+ agent_name: Name of the local agent.
137
+ chunk_size: Override chunk size (default 256 KB).
138
+ """
139
+
140
+ def __init__(
141
+ self,
142
+ home: Path,
143
+ agent_name: str = "anonymous",
144
+ chunk_size: int = CHUNK_SIZE,
145
+ ) -> None:
146
+ self._home = home
147
+ self._agent = agent_name
148
+ self._chunk_size = chunk_size
149
+ self._base_dir = home / "file-transfer"
150
+ self._outbox = self._base_dir / "outbox"
151
+ self._inbox = self._base_dir / "inbox"
152
+ self._completed = self._base_dir / "completed"
153
+
154
+ def initialize(self) -> None:
155
+ """Create the file transfer directory structure."""
156
+ self._outbox.mkdir(parents=True, exist_ok=True)
157
+ self._inbox.mkdir(parents=True, exist_ok=True)
158
+ self._completed.mkdir(parents=True, exist_ok=True)
159
+
160
+ def send(
161
+ self,
162
+ file_path: Path,
163
+ recipient: str,
164
+ encrypt: bool = True,
165
+ ) -> TransferManifest:
166
+ """Prepare a file for transfer by chunking and encrypting.
167
+
168
+ Args:
169
+ file_path: Path to the file to send.
170
+ recipient: Recipient agent name.
171
+ encrypt: Whether to encrypt chunks (default True).
172
+
173
+ Returns:
174
+ TransferManifest with all chunk metadata.
175
+
176
+ Raises:
177
+ FileNotFoundError: If the file doesn't exist.
178
+ ValueError: If the file is empty.
179
+ """
180
+ self.initialize()
181
+
182
+ file_path = Path(file_path)
183
+ if not file_path.exists():
184
+ raise FileNotFoundError(f"File not found: {file_path}")
185
+
186
+ file_data = file_path.read_bytes()
187
+ if not file_data:
188
+ raise ValueError("Cannot transfer empty file")
189
+
190
+ file_hash = hashlib.sha256(file_data).hexdigest()
191
+ total_chunks = (len(file_data) + self._chunk_size - 1) // self._chunk_size
192
+
193
+ manifest = TransferManifest(
194
+ filename=file_path.name,
195
+ file_size=len(file_data),
196
+ file_sha256=file_hash,
197
+ chunk_size=self._chunk_size,
198
+ total_chunks=total_chunks,
199
+ sender=self._agent,
200
+ recipient=recipient,
201
+ )
202
+
203
+ transfer_dir = self._outbox / manifest.transfer_id
204
+ transfer_dir.mkdir(parents=True, exist_ok=True)
205
+
206
+ enc_key = self._get_encryption_key() if encrypt else None
207
+
208
+ for i in range(total_chunks):
209
+ start = i * self._chunk_size
210
+ end = min(start + self._chunk_size, len(file_data))
211
+ chunk_data = file_data[start:end]
212
+ chunk_hash = hashlib.sha256(chunk_data).hexdigest()
213
+
214
+ if encrypt and enc_key is not None:
215
+ chunk_data = self._encrypt_chunk(chunk_data, enc_key)
216
+
217
+ chunk_file = transfer_dir / f"chunk-{i:04d}.enc"
218
+ chunk_file.write_bytes(chunk_data)
219
+
220
+ manifest.chunks.append(ChunkInfo(
221
+ index=i,
222
+ size=end - start,
223
+ sha256=chunk_hash,
224
+ encrypted=encrypt and enc_key is not None,
225
+ sent=True,
226
+ ))
227
+
228
+ # Write manifest
229
+ manifest_path = transfer_dir / "manifest.json"
230
+ manifest_path.write_text(
231
+ manifest.model_dump_json(indent=2),
232
+ encoding="utf-8",
233
+ )
234
+
235
+ logger.info(
236
+ "Prepared transfer %s: %s (%d chunks, %d bytes) -> %s",
237
+ manifest.transfer_id, manifest.filename,
238
+ total_chunks, manifest.file_size, recipient,
239
+ )
240
+
241
+ return manifest
242
+
243
+ def receive(
244
+ self,
245
+ transfer_id: str,
246
+ output_dir: Optional[Path] = None,
247
+ ) -> Path:
248
+ """Receive and reassemble a file transfer.
249
+
250
+ Reads chunks from inbox, decrypts them, verifies integrity,
251
+ and reassembles the original file.
252
+
253
+ Args:
254
+ transfer_id: The transfer ID to receive.
255
+ output_dir: Where to write the assembled file.
256
+ Defaults to inbox/<transfer_id>/.
257
+
258
+ Returns:
259
+ Path to the reassembled file.
260
+
261
+ Raises:
262
+ FileNotFoundError: If manifest not found.
263
+ ValueError: If integrity check fails.
264
+ """
265
+ # Check both inbox and outbox (for local testing)
266
+ transfer_dir = self._inbox / transfer_id
267
+ if not transfer_dir.is_dir():
268
+ transfer_dir = self._outbox / transfer_id
269
+ if not transfer_dir.is_dir():
270
+ raise FileNotFoundError(f"Transfer {transfer_id} not found")
271
+
272
+ manifest_path = transfer_dir / "manifest.json"
273
+ if not manifest_path.exists():
274
+ raise FileNotFoundError(f"Manifest not found for {transfer_id}")
275
+
276
+ manifest = TransferManifest.model_validate_json(
277
+ manifest_path.read_text(encoding="utf-8")
278
+ )
279
+
280
+ enc_key = self._get_encryption_key()
281
+ assembled = bytearray()
282
+
283
+ for chunk_info in sorted(manifest.chunks, key=lambda c: c.index):
284
+ chunk_file = transfer_dir / f"chunk-{chunk_info.index:04d}.enc"
285
+ if not chunk_file.exists():
286
+ raise FileNotFoundError(
287
+ f"Missing chunk {chunk_info.index} for transfer {transfer_id}"
288
+ )
289
+
290
+ chunk_data = chunk_file.read_bytes()
291
+
292
+ if chunk_info.encrypted and enc_key is not None:
293
+ chunk_data = self._decrypt_chunk(chunk_data, enc_key)
294
+
295
+ # Verify chunk integrity
296
+ actual_hash = hashlib.sha256(chunk_data).hexdigest()
297
+ if actual_hash != chunk_info.sha256:
298
+ raise ValueError(
299
+ f"Chunk {chunk_info.index} integrity check failed: "
300
+ f"expected {chunk_info.sha256[:16]}..., got {actual_hash[:16]}..."
301
+ )
302
+
303
+ chunk_info.received = True
304
+ assembled.extend(chunk_data)
305
+
306
+ # Verify complete file integrity
307
+ file_hash = hashlib.sha256(assembled).hexdigest()
308
+ if file_hash != manifest.file_sha256:
309
+ raise ValueError(
310
+ f"File integrity check failed: "
311
+ f"expected {manifest.file_sha256[:16]}..., got {file_hash[:16]}..."
312
+ )
313
+
314
+ # Write assembled file
315
+ dest_dir = output_dir or transfer_dir
316
+ dest_dir.mkdir(parents=True, exist_ok=True)
317
+ output_path = dest_dir / manifest.filename
318
+ output_path.write_bytes(assembled)
319
+
320
+ # Record completion
321
+ manifest.completed_at = datetime.now(timezone.utc)
322
+ receipt = self._completed / f"{transfer_id}.json"
323
+ receipt.write_text(manifest.model_dump_json(indent=2), encoding="utf-8")
324
+
325
+ # Update manifest
326
+ manifest_path.write_text(manifest.model_dump_json(indent=2), encoding="utf-8")
327
+
328
+ logger.info(
329
+ "Received transfer %s: %s (%d bytes, %d chunks)",
330
+ transfer_id, manifest.filename, manifest.file_size, manifest.total_chunks,
331
+ )
332
+
333
+ return output_path
334
+
335
+ def get_manifest(self, transfer_id: str) -> Optional[TransferManifest]:
336
+ """Get the manifest for a transfer.
337
+
338
+ Args:
339
+ transfer_id: The transfer ID.
340
+
341
+ Returns:
342
+ TransferManifest or None if not found.
343
+ """
344
+ for base in (self._outbox, self._inbox):
345
+ manifest_path = base / transfer_id / "manifest.json"
346
+ if manifest_path.exists():
347
+ return TransferManifest.model_validate_json(
348
+ manifest_path.read_text(encoding="utf-8")
349
+ )
350
+ return None
351
+
352
+ def list_transfers(self, direction: Optional[str] = None) -> list[TransferStatus]:
353
+ """List all transfers with progress info.
354
+
355
+ Args:
356
+ direction: Filter by "send" or "receive". None = all.
357
+
358
+ Returns:
359
+ List of TransferStatus objects.
360
+ """
361
+ statuses: list[TransferStatus] = []
362
+
363
+ if direction in (None, "send"):
364
+ for d in self._outbox.iterdir() if self._outbox.is_dir() else []:
365
+ if d.is_dir():
366
+ manifest = self._read_manifest(d)
367
+ if manifest:
368
+ statuses.append(self._manifest_to_status(manifest, "send"))
369
+
370
+ if direction in (None, "receive"):
371
+ for d in self._inbox.iterdir() if self._inbox.is_dir() else []:
372
+ if d.is_dir():
373
+ manifest = self._read_manifest(d)
374
+ if manifest:
375
+ statuses.append(self._manifest_to_status(manifest, "receive"))
376
+
377
+ statuses.sort(key=lambda s: s.created_at or datetime.min, reverse=True)
378
+ return statuses
379
+
380
+ def resume_send(self, transfer_id: str) -> list[int]:
381
+ """Find unsent chunks for a transfer.
382
+
383
+ Args:
384
+ transfer_id: The transfer ID.
385
+
386
+ Returns:
387
+ List of chunk indices that haven't been sent.
388
+ """
389
+ manifest = self.get_manifest(transfer_id)
390
+ if manifest is None:
391
+ return []
392
+ return [c.index for c in manifest.chunks if not c.sent]
393
+
394
+ def resume_receive(self, transfer_id: str) -> list[int]:
395
+ """Find missing chunks for a transfer.
396
+
397
+ Args:
398
+ transfer_id: The transfer ID.
399
+
400
+ Returns:
401
+ List of chunk indices that haven't been received.
402
+ """
403
+ manifest = self.get_manifest(transfer_id)
404
+ if manifest is None:
405
+ return []
406
+
407
+ transfer_dir = self._inbox / transfer_id
408
+ if not transfer_dir.is_dir():
409
+ transfer_dir = self._outbox / transfer_id
410
+
411
+ missing = []
412
+ for c in manifest.chunks:
413
+ chunk_file = transfer_dir / f"chunk-{c.index:04d}.enc"
414
+ if not chunk_file.exists():
415
+ missing.append(c.index)
416
+ return missing
417
+
418
+ def cleanup(self, transfer_id: str) -> bool:
419
+ """Remove all files for a completed transfer.
420
+
421
+ Args:
422
+ transfer_id: The transfer ID to clean up.
423
+
424
+ Returns:
425
+ True if cleaned up, False if not found.
426
+ """
427
+ cleaned = False
428
+ for base in (self._outbox, self._inbox):
429
+ transfer_dir = base / transfer_id
430
+ if transfer_dir.is_dir():
431
+ import shutil
432
+ shutil.rmtree(transfer_dir)
433
+ cleaned = True
434
+ return cleaned
435
+
436
+ def status(self) -> dict[str, Any]:
437
+ """Return file transfer status summary."""
438
+ outbox_count = sum(
439
+ 1 for d in self._outbox.iterdir() if d.is_dir()
440
+ ) if self._outbox.is_dir() else 0
441
+ inbox_count = sum(
442
+ 1 for d in self._inbox.iterdir() if d.is_dir()
443
+ ) if self._inbox.is_dir() else 0
444
+ completed_count = sum(
445
+ 1 for _ in self._completed.glob("*.json")
446
+ ) if self._completed.is_dir() else 0
447
+
448
+ return {
449
+ "outbox_transfers": outbox_count,
450
+ "inbox_transfers": inbox_count,
451
+ "completed": completed_count,
452
+ "base_dir": str(self._base_dir),
453
+ }
454
+
455
+ # -------------------------------------------------------------------
456
+ # Internal helpers
457
+ # -------------------------------------------------------------------
458
+
459
+ def _get_encryption_key(self) -> Optional[bytes]:
460
+ """Get the file transfer encryption key from KMS."""
461
+ try:
462
+ from .kms import KeyStore
463
+
464
+ store = KeyStore(self._home)
465
+ store.initialize()
466
+ key_record = store.derive_service_key("file-transfer")
467
+ return store.get_key_material(key_record.key_id)
468
+ except Exception as exc:
469
+ logger.warning("KMS unavailable for file transfer: %s", exc)
470
+ return None
471
+
472
+ def _encrypt_chunk(self, data: bytes, key: bytes) -> bytes:
473
+ """Encrypt a chunk using Fernet."""
474
+ from .kms import _fernet_encrypt
475
+
476
+ return _fernet_encrypt(data, key)
477
+
478
+ def _decrypt_chunk(self, data: bytes, key: bytes) -> bytes:
479
+ """Decrypt a chunk using Fernet."""
480
+ from .kms import _fernet_decrypt
481
+
482
+ return _fernet_decrypt(data, key)
483
+
484
+ def _read_manifest(self, transfer_dir: Path) -> Optional[TransferManifest]:
485
+ """Read a manifest from a transfer directory."""
486
+ manifest_path = transfer_dir / "manifest.json"
487
+ if not manifest_path.exists():
488
+ return None
489
+ try:
490
+ return TransferManifest.model_validate_json(
491
+ manifest_path.read_text(encoding="utf-8")
492
+ )
493
+ except Exception:
494
+ return None
495
+
496
+ def _manifest_to_status(
497
+ self, manifest: TransferManifest, direction: str,
498
+ ) -> TransferStatus:
499
+ """Convert a manifest to a status summary."""
500
+ done = sum(1 for c in manifest.chunks if c.sent or c.received)
501
+ return TransferStatus(
502
+ transfer_id=manifest.transfer_id,
503
+ filename=manifest.filename,
504
+ file_size=manifest.file_size,
505
+ direction=direction,
506
+ progress=manifest.progress,
507
+ total_chunks=manifest.total_chunks,
508
+ chunks_done=done,
509
+ sender=manifest.sender,
510
+ recipient=manifest.recipient,
511
+ created_at=manifest.created_at,
512
+ )