@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,467 @@
1
+ """Emotion tracker — classifies sentiment after each consciousness loop response.
2
+
3
+ After each LLM response, classifies the sentiment into one of:
4
+ positive, neutral, concerned, excited
5
+
6
+ Stores each classification as a memory entry with ``tag=emotion`` and a
7
+ valence score 0–1. Updates the warmth anchor's ``warmth`` field using a
8
+ 7-day rolling average, triggering a re-calibration every
9
+ ``_WARMTH_UPDATE_EVERY`` records.
10
+
11
+ The LLM path uses a single "1-token" classify call (a minimal prompt that
12
+ asks for one word). If the bridge is unavailable or the call fails, the
13
+ module falls back to keyword-based heuristics so the loop is never blocked.
14
+
15
+ Usage::
16
+
17
+ tracker = EmotionTracker(home=Path("~/.skcapstone"))
18
+ tracker.record(response="I'm happy to help!", sender="alice", bridge=llm_bridge)
19
+ trend = tracker.get_trend(days=7)
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import logging
26
+ import threading
27
+ from datetime import datetime, timedelta, timezone
28
+ from pathlib import Path
29
+ from typing import Any, Optional
30
+
31
+ from pydantic import BaseModel
32
+
33
+ logger = logging.getLogger("skcapstone.emotion")
34
+
35
+ # ---------------------------------------------------------------------------
36
+ # Constants
37
+ # ---------------------------------------------------------------------------
38
+
39
+ EMOTION_LABELS = ("positive", "neutral", "concerned", "excited")
40
+
41
+ # Keyword sets for heuristic classification
42
+ _EXCITED_WORDS = frozenset({
43
+ "exciting", "fascinating", "incredible", "remarkable", "breakthrough",
44
+ "amazing", "extraordinary", "innovative", "revolutionary", "profound",
45
+ "powerful", "fantastic", "thrilled", "eager", "enthusiastic", "curious",
46
+ "excellent", "brilliant", "spectacular", "outstanding", "wow",
47
+ })
48
+ _CONCERNED_WORDS = frozenset({
49
+ "sorry", "apologize", "unfortunately", "unable", "cannot", "error",
50
+ "fail", "problem", "issue", "concern", "worry", "difficult", "trouble",
51
+ "wrong", "broken", "warning", "caution", "careful", "risk", "danger",
52
+ "limited", "unavailable", "unclear", "missing", "blocked", "failed",
53
+ })
54
+ _POSITIVE_WORDS = frozenset({
55
+ "great", "happy", "glad", "wonderful", "perfect", "good", "pleasure",
56
+ "sure", "absolutely", "delighted", "appreciate", "thanks", "helpful",
57
+ "solved", "done", "complete", "succeed", "love", "enjoy", "welcome",
58
+ "nice", "correct", "right", "works", "working", "ready", "success",
59
+ })
60
+
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # Score mapping
64
+ # ---------------------------------------------------------------------------
65
+
66
+ _LABEL_SCORES: dict[str, float] = {
67
+ "positive": 0.85,
68
+ "excited": 0.75,
69
+ "neutral": 0.50,
70
+ "concerned": 0.25,
71
+ }
72
+
73
+
74
+ def _score_from_label(label: str) -> float:
75
+ """Map an emotion label to a valence score 0–1."""
76
+ return _LABEL_SCORES.get(label, 0.50)
77
+
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # Keyword classifier
81
+ # ---------------------------------------------------------------------------
82
+
83
+
84
+ def _keyword_classify(text: str) -> tuple[str, float]:
85
+ """Fast keyword-based sentiment classifier — no LLM required.
86
+
87
+ Args:
88
+ text: Text to classify.
89
+
90
+ Returns:
91
+ Tuple of (label, score) where label is one of EMOTION_LABELS
92
+ and score is the corresponding valence 0–1.
93
+ """
94
+ if not text:
95
+ return "neutral", 0.50
96
+
97
+ words = set(text.lower().split())
98
+ excited_hits = len(words & _EXCITED_WORDS)
99
+ concerned_hits = len(words & _CONCERNED_WORDS)
100
+ positive_hits = len(words & _POSITIVE_WORDS)
101
+
102
+ # Priority: excited ≥ 2 hits > concerned ≥ 2 > positive ≥ 2 > else neutral
103
+ if excited_hits >= 2:
104
+ return "excited", 0.80
105
+ if concerned_hits >= 2:
106
+ return "concerned", 0.25
107
+ if concerned_hits >= 1 and positive_hits == 0 and excited_hits == 0:
108
+ return "concerned", 0.30
109
+ if positive_hits >= 2 or (positive_hits >= 1 and excited_hits >= 1):
110
+ return "positive", 0.85
111
+ if positive_hits >= 1:
112
+ return "positive", 0.70
113
+ if excited_hits >= 1:
114
+ return "excited", 0.72
115
+ return "neutral", 0.50
116
+
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # Data model
120
+ # ---------------------------------------------------------------------------
121
+
122
+
123
+ class EmotionEntry(BaseModel):
124
+ """One emotion classification record.
125
+
126
+ Attributes:
127
+ label: Classified emotion label.
128
+ score: Valence score 0.0–1.0 (higher = more positive).
129
+ sender: Peer who triggered the response being classified.
130
+ timestamp: ISO-8601 UTC timestamp.
131
+ """
132
+
133
+ label: str
134
+ score: float
135
+ sender: str
136
+ timestamp: str
137
+
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # Tracker
141
+ # ---------------------------------------------------------------------------
142
+
143
+
144
+ class EmotionTracker:
145
+ """Tracks per-response emotion, stores as memory, updates warmth anchor.
146
+
147
+ Thread-safe. Persists emotion log to ``{home}/emotion_log.json``.
148
+ Every ``_WARMTH_UPDATE_EVERY`` records the 7-day rolling average is
149
+ recomputed and the warmth anchor is updated via exponential smoothing.
150
+
151
+ Args:
152
+ home: Agent home directory (default: ``~/.skcapstone``).
153
+ """
154
+
155
+ _LOG_FILE = "emotion_log.json"
156
+ _MAX_ENTRIES = 1000 # cap log at this many entries
157
+ _WARMTH_UPDATE_EVERY = 5 # recompute anchor every N records
158
+
159
+ def __init__(self, home: Optional[Path] = None) -> None:
160
+ from skcapstone import AGENT_HOME
161
+
162
+ self._home = (home or Path(AGENT_HOME)).expanduser()
163
+ self._lock = threading.Lock()
164
+ self._counter = 0
165
+
166
+ # ------------------------------------------------------------------
167
+ # Public API
168
+ # ------------------------------------------------------------------
169
+
170
+ def record(
171
+ self,
172
+ response: str,
173
+ sender: str,
174
+ bridge: Optional[Any] = None,
175
+ ) -> EmotionEntry:
176
+ """Classify, persist, and propagate emotion for a single response.
177
+
178
+ Steps:
179
+ 1. Classify sentiment (LLM 1-token call if *bridge* provided,
180
+ otherwise keyword heuristic).
181
+ 2. Append to ``emotion_log.json``.
182
+ 3. Store as a memory entry tagged ``emotion``.
183
+ 4. Every ``_WARMTH_UPDATE_EVERY`` records recompute the 7-day
184
+ rolling average and update the warmth anchor.
185
+
186
+ Args:
187
+ response: LLM response text to classify.
188
+ sender: Name of the peer this response was sent to.
189
+ bridge: Optional ``LLMBridge`` for the 1-token classify call.
190
+
191
+ Returns:
192
+ The created :class:`EmotionEntry`.
193
+ """
194
+ label, score = self._classify(response, bridge)
195
+ entry = EmotionEntry(
196
+ label=label,
197
+ score=score,
198
+ sender=sender,
199
+ timestamp=datetime.now(timezone.utc).isoformat(),
200
+ )
201
+ self._persist(entry)
202
+ self._store_memory(entry)
203
+
204
+ with self._lock:
205
+ self._counter += 1
206
+ should_update = (self._counter % self._WARMTH_UPDATE_EVERY == 0)
207
+
208
+ if should_update:
209
+ self._update_warmth_anchor()
210
+
211
+ logger.debug(
212
+ "Emotion recorded: label=%s score=%.2f sender=%s",
213
+ label, score, sender,
214
+ )
215
+ return entry
216
+
217
+ def get_trend(self, days: int = 7) -> dict:
218
+ """Return emotion trend data over the given lookback window.
219
+
220
+ Args:
221
+ days: Number of days to look back (default 7).
222
+
223
+ Returns:
224
+ Dict with keys:
225
+ - ``window_days``: lookback period
226
+ - ``total_records``: number of entries found
227
+ - ``avg_score``: mean valence score 0–1
228
+ - ``label_counts``: dict of label → count
229
+ - ``dominant_label``: most-frequent label
230
+ - ``trend``: "improving" | "stable" | "declining"
231
+ - ``warmth_recommendation``: avg_score × 10 (anchor scale 0–10)
232
+ - ``entries``: list of raw entry dicts (most-recent first)
233
+ """
234
+ entries = self._load_recent(days)
235
+ if not entries:
236
+ return {
237
+ "window_days": days,
238
+ "total_records": 0,
239
+ "avg_score": 0.50,
240
+ "label_counts": {lbl: 0 for lbl in EMOTION_LABELS},
241
+ "dominant_label": "neutral",
242
+ "trend": "stable",
243
+ "warmth_recommendation": 5.0,
244
+ "entries": [],
245
+ }
246
+
247
+ label_counts: dict[str, int] = {lbl: 0 for lbl in EMOTION_LABELS}
248
+ scores: list[float] = []
249
+ for e in entries:
250
+ label_counts[e.label] = label_counts.get(e.label, 0) + 1
251
+ scores.append(e.score)
252
+
253
+ avg_score = sum(scores) / len(scores)
254
+ dominant = max(label_counts, key=lambda k: label_counts[k])
255
+
256
+ # Trend: compare first-half vs second-half average scores
257
+ mid = len(scores) // 2
258
+ if mid > 0:
259
+ first_avg = sum(scores[:mid]) / mid
260
+ second_avg = sum(scores[mid:]) / (len(scores) - mid)
261
+ delta = second_avg - first_avg
262
+ if delta > 0.05:
263
+ trend = "improving"
264
+ elif delta < -0.05:
265
+ trend = "declining"
266
+ else:
267
+ trend = "stable"
268
+ else:
269
+ trend = "stable"
270
+
271
+ return {
272
+ "window_days": days,
273
+ "total_records": len(entries),
274
+ "avg_score": round(avg_score, 3),
275
+ "label_counts": label_counts,
276
+ "dominant_label": dominant,
277
+ "trend": trend,
278
+ "warmth_recommendation": round(avg_score * 10, 2),
279
+ "entries": [e.model_dump() for e in reversed(entries)],
280
+ }
281
+
282
+ # ------------------------------------------------------------------
283
+ # Internal: classification
284
+ # ------------------------------------------------------------------
285
+
286
+ def _classify(
287
+ self, text: str, bridge: Optional[Any]
288
+ ) -> tuple[str, float]:
289
+ """Classify sentiment using LLM (1-token call) with keyword fallback.
290
+
291
+ Args:
292
+ text: Text to classify.
293
+ bridge: Optional LLMBridge instance.
294
+
295
+ Returns:
296
+ (label, score) tuple.
297
+ """
298
+ if bridge is not None:
299
+ try:
300
+ label = self._llm_classify(text, bridge)
301
+ if label in EMOTION_LABELS:
302
+ return label, _score_from_label(label)
303
+ except Exception as exc:
304
+ logger.debug(
305
+ "LLM sentiment classify failed, using keywords: %s", exc
306
+ )
307
+ return _keyword_classify(text)
308
+
309
+ def _llm_classify(self, text: str, bridge: Any) -> str:
310
+ """Make a minimal 1-token LLM call to classify sentiment.
311
+
312
+ Sends a short prompt asking for a single word classification.
313
+ Uses the FAST task signal so the router selects the lightest model.
314
+
315
+ Args:
316
+ text: Response text (first 400 chars used).
317
+ bridge: LLMBridge instance.
318
+
319
+ Returns:
320
+ First word from the LLM response (lowercased), or "" on failure.
321
+ """
322
+ from skcapstone.model_router import TaskSignal
323
+
324
+ snippet = text[:400].replace('"', "'")
325
+ user_message = (
326
+ "Classify the sentiment of this AI assistant response.\n"
327
+ "Reply with exactly one word from: positive, neutral, concerned, excited\n"
328
+ f'Response: "{snippet}"\n'
329
+ "Sentiment:"
330
+ )
331
+ signal = TaskSignal(
332
+ description="1-token sentiment classification",
333
+ tags=["classification", "fast"],
334
+ estimated_tokens=15,
335
+ )
336
+ raw = bridge.generate(
337
+ system_prompt=(
338
+ "You are a sentiment classifier. "
339
+ "Output exactly one word: positive, neutral, concerned, or excited."
340
+ ),
341
+ user_message=user_message,
342
+ signal=signal,
343
+ skip_cache=False,
344
+ )
345
+ return raw.strip().lower().split()[0] if raw.strip() else ""
346
+
347
+ # ------------------------------------------------------------------
348
+ # Internal: persistence
349
+ # ------------------------------------------------------------------
350
+
351
+ def _persist(self, entry: EmotionEntry) -> None:
352
+ """Append *entry* to the emotion log JSON file (thread-safe)."""
353
+ with self._lock:
354
+ path = self._home / self._LOG_FILE
355
+ try:
356
+ path.parent.mkdir(parents=True, exist_ok=True)
357
+ existing: list[dict] = []
358
+ if path.exists():
359
+ try:
360
+ existing = json.loads(path.read_text(encoding="utf-8"))
361
+ if not isinstance(existing, list):
362
+ existing = []
363
+ except (json.JSONDecodeError, OSError):
364
+ existing = []
365
+ existing.append(entry.model_dump())
366
+ # Trim to cap
367
+ if len(existing) > self._MAX_ENTRIES:
368
+ existing = existing[-self._MAX_ENTRIES :]
369
+ path.write_text(json.dumps(existing, indent=2), encoding="utf-8")
370
+ except Exception as exc:
371
+ logger.warning("Failed to persist emotion entry: %s", exc)
372
+
373
+ def _load_recent(self, days: int) -> list[EmotionEntry]:
374
+ """Load entries from the last *days* days, oldest-first.
375
+
376
+ Args:
377
+ days: Lookback window.
378
+
379
+ Returns:
380
+ List of :class:`EmotionEntry` objects within the window.
381
+ """
382
+ path = self._home / self._LOG_FILE
383
+ if not path.exists():
384
+ return []
385
+ try:
386
+ raw: list[dict] = json.loads(path.read_text(encoding="utf-8"))
387
+ cutoff = (
388
+ datetime.now(timezone.utc) - timedelta(days=days)
389
+ ).isoformat()
390
+ entries: list[EmotionEntry] = []
391
+ for item in raw:
392
+ if item.get("timestamp", "") >= cutoff:
393
+ try:
394
+ entries.append(EmotionEntry(**item))
395
+ except Exception:
396
+ pass
397
+ return entries
398
+ except Exception as exc:
399
+ logger.debug("Failed to load emotion log: %s", exc)
400
+ return []
401
+
402
+ # ------------------------------------------------------------------
403
+ # Internal: side-effects
404
+ # ------------------------------------------------------------------
405
+
406
+ def _store_memory(self, entry: EmotionEntry) -> None:
407
+ """Store *entry* as a skcapstone memory with tag ``emotion``.
408
+
409
+ Memory content encodes label, score, and sender so later searches
410
+ on ``tag=emotion`` surface the full history.
411
+ """
412
+ try:
413
+ from skcapstone.memory_engine import store
414
+
415
+ content = (
416
+ f"Emotion after response to {entry.sender}: "
417
+ f"{entry.label} (score={entry.score:.2f})"
418
+ )
419
+ store(
420
+ home=self._home,
421
+ content=content,
422
+ tags=["emotion", f"emotion:{entry.label}", f"peer:{entry.sender}"],
423
+ source="emotion_tracker",
424
+ importance=0.3,
425
+ metadata={
426
+ "label": entry.label,
427
+ "score": entry.score,
428
+ "sender": entry.sender,
429
+ "timestamp": entry.timestamp,
430
+ },
431
+ )
432
+ except Exception as exc:
433
+ logger.debug("Failed to store emotion memory: %s", exc)
434
+
435
+ def _update_warmth_anchor(self) -> None:
436
+ """Recompute 7-day rolling average and nudge the warmth anchor.
437
+
438
+ Converts the average valence score (0–1) to the anchor's 0–10 warmth
439
+ scale and calls :func:`~skcapstone.warmth_anchor.update_anchor` which
440
+ applies exponential smoothing (30% new, 70% history).
441
+ """
442
+ try:
443
+ trend = self.get_trend(days=7)
444
+ new_warmth = trend["warmth_recommendation"]
445
+ if new_warmth <= 0:
446
+ return
447
+
448
+ from skcapstone.warmth_anchor import update_anchor
449
+
450
+ updated = update_anchor(
451
+ home=self._home,
452
+ warmth=new_warmth,
453
+ feeling=(
454
+ f"7-day emotion avg: {trend['dominant_label']} "
455
+ f"(score={trend['avg_score']:.2f}, trend={trend['trend']})"
456
+ ),
457
+ )
458
+ logger.debug(
459
+ "Warmth anchor updated via emotion: warmth=%.2f "
460
+ "(7-day avg_score=%.3f dominant=%s trend=%s)",
461
+ updated.get("warmth", new_warmth),
462
+ trend["avg_score"],
463
+ trend["dominant_label"],
464
+ trend["trend"],
465
+ )
466
+ except Exception as exc:
467
+ logger.debug("Failed to update warmth anchor from emotion: %s", exc)