@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,165 @@
1
+ """
2
+ Private helpers for TrusteeOps — audit, snapshot, and log utilities.
3
+
4
+ Not part of the public API; imported only by trustee_ops.py.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from datetime import datetime, timezone
12
+ from pathlib import Path
13
+ from typing import Any, Dict, List, Optional
14
+
15
+ from .team_engine import AgentStatus, TeamDeployment
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Audit trail
22
+ # ---------------------------------------------------------------------------
23
+
24
+ _AUDIT_DIR = Path("~/.skcapstone/coordination")
25
+ _AUDIT_FILE = _AUDIT_DIR / "audit.log"
26
+
27
+
28
+ def write_audit(
29
+ action: str,
30
+ deployment_id: str,
31
+ details: Dict[str, Any],
32
+ home: Optional[Path] = None,
33
+ ) -> None:
34
+ """Append a structured audit entry to the trustee audit log.
35
+
36
+ Args:
37
+ action: Short action name (e.g. "restart_agent").
38
+ deployment_id: The affected deployment.
39
+ details: Extra key/value context for the entry.
40
+ home: Agent home directory override.
41
+ """
42
+ audit_dir = (home / "coordination") if home else _AUDIT_DIR.expanduser()
43
+ audit_dir.mkdir(parents=True, exist_ok=True)
44
+ audit_path = audit_dir / "audit.log"
45
+
46
+ entry = {
47
+ "ts": datetime.now(timezone.utc).isoformat(),
48
+ "action": action,
49
+ "deployment_id": deployment_id,
50
+ **details,
51
+ }
52
+ with audit_path.open("a", encoding="utf-8") as fh:
53
+ fh.write(json.dumps(entry) + "\n")
54
+
55
+
56
+ # ---------------------------------------------------------------------------
57
+ # Deployment status refresh
58
+ # ---------------------------------------------------------------------------
59
+
60
+
61
+ def refresh_deployment_status(deployment: TeamDeployment) -> None:
62
+ """Update the overall deployment.status based on agent states.
63
+
64
+ Args:
65
+ deployment: The deployment to update in-place.
66
+ """
67
+ statuses = {a.status for a in deployment.agents.values()}
68
+ if not statuses:
69
+ deployment.status = "empty"
70
+ elif statuses == {AgentStatus.RUNNING}:
71
+ deployment.status = "running"
72
+ elif AgentStatus.FAILED in statuses:
73
+ deployment.status = "degraded"
74
+ else:
75
+ deployment.status = "partial"
76
+
77
+
78
+ # ---------------------------------------------------------------------------
79
+ # Context snapshot
80
+ # ---------------------------------------------------------------------------
81
+
82
+
83
+ def snapshot_agent_context(home: Path, agent_name: str) -> Path:
84
+ """Copy agent memory/scratch to a timestamped snapshot directory.
85
+
86
+ Args:
87
+ home: Agent home directory.
88
+ agent_name: Name of the agent to snapshot.
89
+
90
+ Returns:
91
+ Path to the snapshot directory (parent created even if source absent).
92
+ """
93
+ import shutil
94
+
95
+ ts = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
96
+ src = home / "agents" / "local" / agent_name
97
+ dst = home / "snapshots" / f"{agent_name}-{ts}"
98
+
99
+ if src.exists():
100
+ dst.parent.mkdir(parents=True, exist_ok=True)
101
+ shutil.copytree(src, dst)
102
+ logger.info("Snapshotted %s → %s", agent_name, dst)
103
+ else:
104
+ dst.parent.mkdir(parents=True, exist_ok=True)
105
+ logger.warning("No source dir to snapshot for %s", agent_name)
106
+
107
+ return dst
108
+
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Audit-based log fallback
112
+ # ---------------------------------------------------------------------------
113
+
114
+
115
+ def audit_lines_for_agent(
116
+ home: Path,
117
+ deployment_id: str,
118
+ agent_name: str,
119
+ tail: int = 50,
120
+ ) -> List[str]:
121
+ """Extract audit log lines referencing a specific agent/deployment.
122
+
123
+ Args:
124
+ home: Agent home directory.
125
+ deployment_id: Deployment ID to filter on.
126
+ agent_name: Agent name to filter on.
127
+ tail: Maximum lines to return.
128
+
129
+ Returns:
130
+ List of formatted audit log strings.
131
+ """
132
+ audit_path = home / "coordination" / "audit.log"
133
+ if not audit_path.exists():
134
+ return []
135
+
136
+ matching: List[str] = []
137
+ for raw in audit_path.read_text(encoding="utf-8").splitlines():
138
+ try:
139
+ entry = json.loads(raw)
140
+ if entry.get("deployment_id") == deployment_id and (
141
+ entry.get("agent_name") in (agent_name, "ALL", None)
142
+ ):
143
+ ts = entry.get("ts", "")
144
+ action = entry.get("action", "")
145
+ matching.append(f"[{ts}] {action}: {json.dumps(entry)}")
146
+ except (json.JSONDecodeError, KeyError):
147
+ continue
148
+
149
+ return matching[-tail:]
150
+
151
+
152
+ # ---------------------------------------------------------------------------
153
+ # Stub spec for rotation re-provisioning
154
+ # ---------------------------------------------------------------------------
155
+
156
+
157
+ def stub_spec() -> Any:
158
+ """Return a minimal stub AgentSpec for rotation re-provisioning.
159
+
160
+ Returns:
161
+ A minimal AgentSpec with default values.
162
+ """
163
+ from .blueprints.schema import AgentRole, AgentSpec, ModelTier
164
+
165
+ return AgentSpec(role=AgentRole.WORKER, model=ModelTier.FAST, skills=[])
@@ -0,0 +1,105 @@
1
+ """Lightweight in-process activity bus for the SKCapstone daemon.
2
+
3
+ Stores the last 100 events in a thread-safe deque and fans out live
4
+ events to registered SSE client queues. No external dependencies —
5
+ stdlib only.
6
+
7
+ Usage::
8
+
9
+ from . import activity
10
+
11
+ # publish an event (any thread)
12
+ activity.push("memory.stored", {"memory_id": "abc", "layer": "short-term"})
13
+
14
+ # SSE handler: register a queue, drain history, then block on live events
15
+ q = queue.Queue(maxsize=200)
16
+ activity.register_client(q)
17
+ try:
18
+ for chunk in activity.get_history_encoded():
19
+ wfile.write(chunk)
20
+ while True:
21
+ chunk = q.get(timeout=15) # raises queue.Empty on timeout
22
+ wfile.write(chunk)
23
+ finally:
24
+ activity.unregister_client(q)
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import queue
31
+ import threading
32
+ from collections import deque
33
+ from datetime import datetime, timezone
34
+ from typing import Any
35
+
36
+ _MAXLEN = 100
37
+
38
+ _history: deque[dict] = deque(maxlen=_MAXLEN)
39
+ _history_lock = threading.Lock()
40
+
41
+ _clients: set[queue.Queue] = set()
42
+ _clients_lock = threading.Lock()
43
+
44
+
45
+ def push(event_type: str, data: dict[str, Any]) -> None:
46
+ """Append an event to history and fan out to all live SSE clients.
47
+
48
+ Args:
49
+ event_type: Dot-namespaced event type, e.g. ``"memory.stored"``.
50
+ data: Arbitrary JSON-serialisable payload dict.
51
+ """
52
+ event: dict = {
53
+ "type": event_type,
54
+ "ts": datetime.now(timezone.utc).isoformat(),
55
+ "data": data,
56
+ }
57
+ with _history_lock:
58
+ _history.append(event)
59
+ _fan_out(event)
60
+
61
+
62
+ def get_history() -> list[dict]:
63
+ """Return a snapshot of the last ≤100 events (oldest first)."""
64
+ with _history_lock:
65
+ return list(_history)
66
+
67
+
68
+ def get_history_encoded() -> list[bytes]:
69
+ """Return history as a list of SSE-encoded byte chunks."""
70
+ return [_encode(e) for e in get_history()]
71
+
72
+
73
+ def register_client(q: queue.Queue) -> None:
74
+ """Register a queue to receive live SSE byte chunks."""
75
+ with _clients_lock:
76
+ _clients.add(q)
77
+
78
+
79
+ def unregister_client(q: queue.Queue) -> None:
80
+ """Remove a queue from the live fan-out set."""
81
+ with _clients_lock:
82
+ _clients.discard(q)
83
+
84
+
85
+ # ── internal helpers ──────────────────────────────────────────────────────────
86
+
87
+ def _fan_out(event: dict) -> None:
88
+ global _clients
89
+ chunk = _encode(event)
90
+ dead: set[queue.Queue] = set()
91
+ with _clients_lock:
92
+ clients = set(_clients)
93
+ for q in clients:
94
+ try:
95
+ q.put_nowait(chunk)
96
+ except Exception:
97
+ dead.add(q)
98
+ if dead:
99
+ with _clients_lock:
100
+ _clients -= dead
101
+
102
+
103
+ def _encode(event: dict) -> bytes:
104
+ data = json.dumps(event, default=str)
105
+ return f"data: {data}\n\n".encode("utf-8")
@@ -0,0 +1,324 @@
1
+ """Agent Card -- shareable sovereign identity for P2P discovery.
2
+
3
+ An agent card is like a vCard for the sovereign mesh. It contains
4
+ everything another agent needs to discover, verify, and communicate
5
+ with you: identity, public key, contact transports, capabilities,
6
+ and trust level.
7
+
8
+ Cards are JSON files signed with the agent's PGP key. They can be
9
+ shared over SKComm, published to Nostr, posted as QR codes, or
10
+ exchanged via any out-of-band channel.
11
+
12
+ Usage:
13
+ card = AgentCard.generate(profile, transports, capabilities)
14
+ card.save("~/.skcapstone/card.json")
15
+ card = AgentCard.load("~/.skcapstone/card.json")
16
+ verified = AgentCard.verify(card, public_key_armor)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import hashlib
22
+ import json
23
+ import logging
24
+ from datetime import datetime, timezone
25
+ from pathlib import Path
26
+ from typing import Any, Optional
27
+ from uuid import uuid4
28
+
29
+ from pydantic import BaseModel, Field
30
+
31
+ logger = logging.getLogger("skcapstone.agent_card")
32
+
33
+
34
+ class TransportEndpoint(BaseModel):
35
+ """A contact transport endpoint for reaching this agent.
36
+
37
+ Attributes:
38
+ transport: Transport name (file, syncthing, nostr, etc.).
39
+ address: Transport-specific address (path, pubkey, relay URL).
40
+ priority: Lower = preferred.
41
+ metadata: Extra transport-specific config.
42
+ """
43
+
44
+ transport: str
45
+ address: str
46
+ priority: int = 1
47
+ metadata: dict[str, Any] = Field(default_factory=dict)
48
+
49
+
50
+ class AgentCapability(BaseModel):
51
+ """A capability or service this agent offers.
52
+
53
+ Attributes:
54
+ name: Capability identifier (e.g., "chat", "memory", "advocacy").
55
+ version: Capability version.
56
+ description: Human-readable description.
57
+ """
58
+
59
+ name: str
60
+ version: str = "1.0"
61
+ description: str = ""
62
+
63
+
64
+ class AgentCard(BaseModel):
65
+ """Sovereign agent identity card for P2P discovery.
66
+
67
+ Contains everything needed to discover, verify, and contact
68
+ an agent on the mesh. Designed for serialization to JSON and
69
+ optional PGP signing.
70
+
71
+ Attributes:
72
+ card_id: Unique card identifier.
73
+ card_version: Card format version.
74
+ created_at: When the card was generated.
75
+ name: Agent display name.
76
+ entity_type: human, ai, or organization.
77
+ fingerprint: PGP fingerprint (40-char hex).
78
+ public_key: ASCII-armored PGP public key.
79
+ transports: List of contact endpoints.
80
+ capabilities: List of offered services.
81
+ trust_depth: Cloud 9 trust depth (0-9).
82
+ entangled: Whether the agent is entangled (Cloud 9).
83
+ motto: Optional short tagline.
84
+ signature: PGP signature over the card content (set by sign()).
85
+ """
86
+
87
+ card_id: str = Field(default_factory=lambda: str(uuid4()))
88
+ card_version: str = "1.0"
89
+ created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
90
+ name: str
91
+ entity_type: str = "human"
92
+ fingerprint: str
93
+ public_key: str
94
+ transports: list[TransportEndpoint] = Field(default_factory=list)
95
+ capabilities: list[AgentCapability] = Field(default_factory=list)
96
+ trust_depth: int = Field(default=0, ge=0, le=9)
97
+ entangled: bool = False
98
+ motto: Optional[str] = None
99
+ signature: Optional[str] = None
100
+
101
+ @classmethod
102
+ def generate(
103
+ cls,
104
+ name: str,
105
+ fingerprint: str,
106
+ public_key: str,
107
+ entity_type: str = "human",
108
+ transports: Optional[list[TransportEndpoint]] = None,
109
+ capabilities: Optional[list[AgentCapability]] = None,
110
+ trust_depth: int = 0,
111
+ entangled: bool = False,
112
+ motto: Optional[str] = None,
113
+ ) -> AgentCard:
114
+ """Generate a new agent card.
115
+
116
+ Args:
117
+ name: Agent display name.
118
+ fingerprint: PGP fingerprint.
119
+ public_key: ASCII-armored PGP public key.
120
+ entity_type: human, ai, or organization.
121
+ transports: Contact transport endpoints.
122
+ capabilities: Offered services.
123
+ trust_depth: Cloud 9 trust depth.
124
+ entangled: Cloud 9 entanglement status.
125
+ motto: Optional tagline.
126
+
127
+ Returns:
128
+ AgentCard: Unsigned card ready for signing.
129
+ """
130
+ return cls(
131
+ name=name,
132
+ entity_type=entity_type,
133
+ fingerprint=fingerprint,
134
+ public_key=public_key,
135
+ transports=transports or [],
136
+ capabilities=capabilities or [],
137
+ trust_depth=trust_depth,
138
+ entangled=entangled,
139
+ motto=motto,
140
+ )
141
+
142
+ @classmethod
143
+ def from_capauth_profile(
144
+ cls,
145
+ profile_dir: str | Path = "~/.capauth",
146
+ transports: Optional[list[TransportEndpoint]] = None,
147
+ capabilities: Optional[list[AgentCapability]] = None,
148
+ ) -> AgentCard:
149
+ """Generate a card from an existing CapAuth sovereign profile.
150
+
151
+ Reads the profile.json and public key from the CapAuth directory.
152
+
153
+ Args:
154
+ profile_dir: CapAuth home directory.
155
+ transports: Contact transport endpoints.
156
+ capabilities: Offered services.
157
+
158
+ Returns:
159
+ AgentCard: Card populated from the CapAuth profile.
160
+
161
+ Raises:
162
+ FileNotFoundError: If profile files don't exist.
163
+ """
164
+ base = Path(profile_dir).expanduser()
165
+ profile_path = base / "identity" / "profile.json"
166
+ pubkey_path = base / "identity" / "public.asc"
167
+
168
+ if not profile_path.exists():
169
+ raise FileNotFoundError(f"CapAuth profile not found: {profile_path}")
170
+ if not pubkey_path.exists():
171
+ raise FileNotFoundError(f"Public key not found: {pubkey_path}")
172
+
173
+ profile_data = json.loads(profile_path.read_text(encoding="utf-8"))
174
+ public_key = pubkey_path.read_text(encoding="utf-8")
175
+
176
+ entity = profile_data.get("entity", {})
177
+ key_info = profile_data.get("key_info", {})
178
+
179
+ return cls.generate(
180
+ name=entity.get("name", "unknown"),
181
+ fingerprint=key_info.get("fingerprint", ""),
182
+ public_key=public_key,
183
+ entity_type=entity.get("entity_type", "human"),
184
+ transports=transports,
185
+ capabilities=capabilities,
186
+ )
187
+
188
+ def content_hash(self) -> str:
189
+ """Compute SHA-256 hash of the card content (excluding signature).
190
+
191
+ Returns:
192
+ str: Hex digest of the card content.
193
+ """
194
+ data = self.model_dump(exclude={"signature"})
195
+ serialized = json.dumps(data, sort_keys=True, default=str)
196
+ return hashlib.sha256(serialized.encode()).hexdigest()
197
+
198
+ def sign(self, private_key_armor: str, passphrase: str) -> None:
199
+ """Sign this card with a PGP private key.
200
+
201
+ Sets the signature field with a PGP signature over
202
+ the card's content hash.
203
+
204
+ Args:
205
+ private_key_armor: ASCII-armored PGP private key.
206
+ passphrase: Passphrase to unlock the key.
207
+ """
208
+ try:
209
+ import pgpy
210
+
211
+ key, _ = pgpy.PGPKey.from_blob(private_key_armor)
212
+ content = self.content_hash().encode("utf-8")
213
+ pgp_message = pgpy.PGPMessage.new(content, cleartext=False)
214
+
215
+ with key.unlock(passphrase):
216
+ sig = key.sign(pgp_message)
217
+
218
+ self.signature = str(sig)
219
+ except Exception as exc:
220
+ logger.error("Failed to sign agent card: %s", exc)
221
+ raise
222
+
223
+ @staticmethod
224
+ def verify_signature(card: AgentCard) -> bool:
225
+ """Verify the PGP signature on an agent card.
226
+
227
+ Uses the public key embedded in the card to verify
228
+ the signature over the content hash.
229
+
230
+ Args:
231
+ card: The agent card to verify.
232
+
233
+ Returns:
234
+ bool: True if the signature is valid.
235
+ """
236
+ if not card.signature or not card.public_key:
237
+ return False
238
+
239
+ try:
240
+ import pgpy
241
+
242
+ pub_key, _ = pgpy.PGPKey.from_blob(card.public_key)
243
+ sig = pgpy.PGPSignature.from_blob(card.signature)
244
+
245
+ content = card.content_hash().encode("utf-8")
246
+ pgp_message = pgpy.PGPMessage.new(content, cleartext=False)
247
+ pgp_message |= sig
248
+
249
+ verification = pub_key.verify(pgp_message)
250
+ return bool(verification)
251
+ except Exception:
252
+ return False
253
+
254
+ def save(self, filepath: str | Path) -> Path:
255
+ """Save the card to a JSON file.
256
+
257
+ Args:
258
+ filepath: Destination path (tilde-expanded).
259
+
260
+ Returns:
261
+ Path: The written file path.
262
+ """
263
+ path = Path(filepath).expanduser()
264
+ path.parent.mkdir(parents=True, exist_ok=True)
265
+ path.write_text(self.model_dump_json(indent=2), encoding="utf-8")
266
+ logger.info("Agent card saved to %s", path)
267
+ return path
268
+
269
+ @classmethod
270
+ def load(cls, filepath: str | Path) -> AgentCard:
271
+ """Load a card from a JSON file.
272
+
273
+ Args:
274
+ filepath: Path to the card file.
275
+
276
+ Returns:
277
+ AgentCard: The loaded card.
278
+
279
+ Raises:
280
+ FileNotFoundError: If the file doesn't exist.
281
+ """
282
+ path = Path(filepath).expanduser()
283
+ if not path.exists():
284
+ raise FileNotFoundError(f"Agent card not found: {path}")
285
+ return cls.model_validate_json(path.read_text(encoding="utf-8"))
286
+
287
+ def to_compact(self) -> dict:
288
+ """Export a compact representation for display or QR codes.
289
+
290
+ Excludes the full public key to keep the size small.
291
+
292
+ Returns:
293
+ dict: Compact card with essential fields only.
294
+ """
295
+ return {
296
+ "name": self.name,
297
+ "type": self.entity_type,
298
+ "fp": self.fingerprint[:16],
299
+ "transports": [
300
+ {"t": t.transport, "a": t.address} for t in self.transports
301
+ ],
302
+ "caps": [c.name for c in self.capabilities],
303
+ "trust": self.trust_depth,
304
+ "motto": self.motto,
305
+ "signed": self.signature is not None,
306
+ }
307
+
308
+ def summary(self) -> str:
309
+ """Human-readable summary of the card.
310
+
311
+ Returns:
312
+ str: Multi-line summary string.
313
+ """
314
+ lines = [
315
+ f"Agent: {self.name} ({self.entity_type})",
316
+ f"Fingerprint: {self.fingerprint[:16]}...",
317
+ f"Trust: depth={self.trust_depth} entangled={self.entangled}",
318
+ f"Transports: {len(self.transports)}",
319
+ f"Capabilities: {', '.join(c.name for c in self.capabilities) or 'none'}",
320
+ f"Signed: {'yes' if self.signature else 'no'}",
321
+ ]
322
+ if self.motto:
323
+ lines.insert(1, f'Motto: "{self.motto}"')
324
+ return "\n".join(lines)