@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,448 @@
1
+ """DID (Decentralized Identity) MCP tools.
2
+
3
+ Exposes four tools:
4
+ did_show — Generate and display DID documents (one tier or all)
5
+ did_verify_peer — Verify a peer's did:key matches their public key
6
+ did_publish — Write DID files to disk
7
+ did_identity_card — Full sovereign identity card (local-only)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json as _json
13
+ import os as _os
14
+ import socket as _socket
15
+ from pathlib import Path
16
+
17
+ from mcp.types import TextContent, Tool
18
+
19
+ from ._helpers import _error_response, _home, _json_response
20
+
21
+ TOOLS: list[Tool] = [
22
+ Tool(
23
+ name="did_show",
24
+ description=(
25
+ "Generate and display DID (Decentralized Identity) documents "
26
+ "for the current agent. Supports three tiers: "
27
+ "'key' (did:key, self-contained, zero infrastructure), "
28
+ "'mesh' (did:web via Tailscale Serve, mesh-private only), "
29
+ "'public' (did:web:skworld.io, minimal — public key + name only), "
30
+ "or 'all' to display all three tiers at once."
31
+ ),
32
+ inputSchema={
33
+ "type": "object",
34
+ "properties": {
35
+ "tier": {
36
+ "type": "string",
37
+ "enum": ["key", "mesh", "public", "all"],
38
+ "description": "Which DID tier to show (default: all)",
39
+ },
40
+ "tailnet_hostname": {
41
+ "type": "string",
42
+ "description": "Tailscale hostname for Tier 2 document (auto-detected if omitted)",
43
+ },
44
+ "tailnet_name": {
45
+ "type": "string",
46
+ "description": "Tailnet magic-DNS suffix, e.g. tailnet-xyz.ts.net",
47
+ },
48
+ },
49
+ "required": [],
50
+ },
51
+ ),
52
+ Tool(
53
+ name="did_verify_peer",
54
+ description=(
55
+ "Verify a peer's DID by computing their did:key from the public key "
56
+ "stored in ~/.skcapstone/peers/{name}.json and comparing against any "
57
+ "cached did_key. Also writes the computed did_key back to the peer file."
58
+ ),
59
+ inputSchema={
60
+ "type": "object",
61
+ "properties": {
62
+ "name": {
63
+ "type": "string",
64
+ "description": "Peer name (must match a file in ~/.skcapstone/peers/)",
65
+ },
66
+ },
67
+ "required": ["name"],
68
+ },
69
+ ),
70
+ Tool(
71
+ name="did_publish",
72
+ description=(
73
+ "Generate all DID tiers and write them to disk. "
74
+ "By default, writes all three tiers including the public Tier 3 document. "
75
+ "Set publish_public=false to opt out of Tier 3 generation — "
76
+ "only Tier 1 (did:key) and Tier 2 (mesh) will be written. "
77
+ "The choice is persisted to ~/.skcapstone/did/policy.json."
78
+ ),
79
+ inputSchema={
80
+ "type": "object",
81
+ "properties": {
82
+ "publish_public": {
83
+ "type": "boolean",
84
+ "description": (
85
+ "Whether to generate the Tier 3 public DID document (default: true). "
86
+ "Set false to keep your identity private — only did:key + mesh tier."
87
+ ),
88
+ },
89
+ "tailnet_hostname": {
90
+ "type": "string",
91
+ "description": "Tailscale hostname for Tier 2 document",
92
+ },
93
+ "tailnet_name": {
94
+ "type": "string",
95
+ "description": "Tailnet magic-DNS suffix",
96
+ },
97
+ "org_domain": {
98
+ "type": "string",
99
+ "description": "Organisation domain for Tier 3 (default: skworld.io)",
100
+ },
101
+ "agent_slug": {
102
+ "type": "string",
103
+ "description": "URL-safe agent slug (default: lowercased entity name)",
104
+ },
105
+ },
106
+ "required": [],
107
+ },
108
+ ),
109
+ Tool(
110
+ name="did_policy",
111
+ description=(
112
+ "View or set the DID publication policy for this agent. "
113
+ "Controls whether Tier 3 (public) DID documents are generated. "
114
+ "Default: publish_public=true. "
115
+ "Set publish_public=false to opt out — identity stays private (did:key + mesh only). "
116
+ "Policy is stored at ~/.skcapstone/did/policy.json."
117
+ ),
118
+ inputSchema={
119
+ "type": "object",
120
+ "properties": {
121
+ "publish_public": {
122
+ "type": "boolean",
123
+ "description": "Set to false to opt out of public Tier 3 DID. Omit to view current policy.",
124
+ },
125
+ },
126
+ "required": [],
127
+ },
128
+ ),
129
+ Tool(
130
+ name="did_identity_card",
131
+ description=(
132
+ "Generate a full sovereign identity card combining the DID anchor, "
133
+ "entity info, soul vibe/core traits, and capabilities. "
134
+ "This is a LOCAL-ONLY artifact — never published to the internet. "
135
+ "Used to render the agent's identity card on skworld.io."
136
+ ),
137
+ inputSchema={
138
+ "type": "object",
139
+ "properties": {
140
+ "include_soul": {
141
+ "type": "boolean",
142
+ "description": "Include soul vibe and core traits (default: true)",
143
+ },
144
+ },
145
+ "required": [],
146
+ },
147
+ ),
148
+ ]
149
+
150
+
151
+ # ---------------------------------------------------------------------------
152
+ # Policy helpers (opt-out of public Tier 3 publishing)
153
+ # ---------------------------------------------------------------------------
154
+
155
+ _POLICY_DEFAULT = {"publish_public": True}
156
+
157
+
158
+ def _policy_path() -> Path:
159
+ return _home() / "did" / "policy.json"
160
+
161
+
162
+ def _load_policy() -> dict:
163
+ """Load publication policy from disk; return default if missing."""
164
+ p = _policy_path()
165
+ if p.exists():
166
+ try:
167
+ return _json.loads(p.read_text(encoding="utf-8"))
168
+ except Exception:
169
+ pass
170
+ return dict(_POLICY_DEFAULT)
171
+
172
+
173
+ def _save_policy(policy: dict) -> None:
174
+ """Persist publication policy to disk."""
175
+ from datetime import datetime, timezone
176
+ p = _policy_path()
177
+ p.parent.mkdir(parents=True, exist_ok=True)
178
+ policy["updated_at"] = datetime.now(timezone.utc).isoformat()
179
+ p.write_text(_json.dumps(policy, indent=2), encoding="utf-8")
180
+
181
+
182
+ # ---------------------------------------------------------------------------
183
+ # Helpers
184
+ # ---------------------------------------------------------------------------
185
+
186
+
187
+ def _resolve_tailnet(tailnet_hostname: str, tailnet_name: str) -> tuple[str, str]:
188
+ """Fill in missing tailnet params from environment / hostname."""
189
+ if not tailnet_hostname:
190
+ tailnet_hostname = _os.environ.get("SKWORLD_HOSTNAME", "")
191
+ if not tailnet_hostname:
192
+ try:
193
+ tailnet_hostname = _socket.gethostname()
194
+ except Exception:
195
+ pass
196
+ if not tailnet_name:
197
+ tailnet_name = _os.environ.get("SKWORLD_TAILNET", "")
198
+ return tailnet_hostname, tailnet_name
199
+
200
+
201
+ # ---------------------------------------------------------------------------
202
+ # Handlers
203
+ # ---------------------------------------------------------------------------
204
+
205
+
206
+ async def _handle_did_show(args: dict) -> list[TextContent]:
207
+ """Generate and display DID documents."""
208
+ tier = args.get("tier", "all")
209
+ hostname, tailnet = _resolve_tailnet(
210
+ args.get("tailnet_hostname", ""),
211
+ args.get("tailnet_name", ""),
212
+ )
213
+
214
+ try:
215
+ from capauth.did import DIDDocumentGenerator, DIDTier # type: ignore[import]
216
+ except ImportError as exc:
217
+ return _error_response(f"capauth.did not available: {exc}")
218
+
219
+ try:
220
+ gen = DIDDocumentGenerator.from_profile()
221
+ except Exception as exc:
222
+ return _error_response(f"Could not load CapAuth profile: {exc}")
223
+
224
+ kw = dict(tailnet_hostname=hostname, tailnet_name=tailnet)
225
+
226
+ if tier == "all":
227
+ docs = gen.generate_all(**kw)
228
+ result = {
229
+ "did_key": gen._ctx.did_key_id,
230
+ "fingerprint": gen._ctx.fingerprint,
231
+ "name": gen._ctx.name,
232
+ "key": docs[DIDTier.KEY],
233
+ "mesh": docs[DIDTier.WEB_MESH],
234
+ "public": docs[DIDTier.WEB_PUBLIC],
235
+ }
236
+ elif tier == "key":
237
+ doc = gen.generate(DIDTier.KEY, **kw)
238
+ result = {"tier": "key", "did_key": gen._ctx.did_key_id, "document": doc}
239
+ elif tier == "mesh":
240
+ doc = gen.generate(DIDTier.WEB_MESH, **kw)
241
+ result = {"tier": "mesh", "document": doc}
242
+ elif tier == "public":
243
+ doc = gen.generate(DIDTier.WEB_PUBLIC, **kw)
244
+ result = {"tier": "public", "document": doc}
245
+ else:
246
+ return _error_response(f"Unknown tier '{tier}'. Use: key, mesh, public, all")
247
+
248
+ return _json_response(result)
249
+
250
+
251
+ async def _handle_did_verify_peer(args: dict) -> list[TextContent]:
252
+ """Verify peer DID against their public key."""
253
+ name = args.get("name", "").strip()
254
+ if not name:
255
+ return _error_response("name is required")
256
+
257
+ home = _home()
258
+ peers_dir = home / "peers"
259
+ peer_file = peers_dir / f"{name}.json"
260
+
261
+ if not peer_file.exists():
262
+ return _error_response(f"Peer file not found: {peer_file}")
263
+
264
+ try:
265
+ peer_data = _json.loads(peer_file.read_text(encoding="utf-8"))
266
+ except Exception as exc:
267
+ return _error_response(f"Could not read peer file: {exc}")
268
+
269
+ pub_armor = peer_data.get("public_key") or peer_data.get("public_key_armor")
270
+ if not pub_armor:
271
+ return _json_response({
272
+ "name": name,
273
+ "verified": False,
274
+ "cached_did_key": peer_data.get("did_key"),
275
+ "detail": "No public_key in peer file — cannot compute did:key",
276
+ })
277
+
278
+ try:
279
+ from capauth.did import ( # type: ignore[import]
280
+ _compute_did_key,
281
+ _pgp_armor_to_rsa_numbers,
282
+ _rsa_numbers_to_der,
283
+ )
284
+ n, e = _pgp_armor_to_rsa_numbers(pub_armor)
285
+ computed = _compute_did_key(_rsa_numbers_to_der(n, e))
286
+ except Exception as exc:
287
+ return _error_response(f"DID computation failed: {exc}")
288
+
289
+ cached = peer_data.get("did_key")
290
+ match = (cached == computed) if cached else None
291
+
292
+ # Cache computed did:key back to peer file
293
+ if not cached or not match:
294
+ peer_data["did_key"] = computed
295
+ try:
296
+ peer_file.write_text(
297
+ _json.dumps(peer_data, indent=2, default=str),
298
+ encoding="utf-8",
299
+ )
300
+ except Exception:
301
+ pass
302
+
303
+ return _json_response({
304
+ "name": name,
305
+ "fingerprint": peer_data.get("fingerprint"),
306
+ "computed_did_key": computed,
307
+ "cached_did_key": cached,
308
+ "match": match,
309
+ "verified": True,
310
+ "detail": (
311
+ "did:key computed from public key"
312
+ + (" (matches cached)" if match else " (cache updated)")
313
+ ),
314
+ })
315
+
316
+
317
+ async def _handle_did_publish(args: dict) -> list[TextContent]:
318
+ """Write DID tiers to disk, respecting publication policy."""
319
+ hostname, tailnet = _resolve_tailnet(
320
+ args.get("tailnet_hostname", ""),
321
+ args.get("tailnet_name", ""),
322
+ )
323
+ org_domain = args.get("org_domain", "skworld.io")
324
+ agent_slug = args.get("agent_slug", "")
325
+
326
+ # Resolve publish_public: explicit arg overrides persisted policy
327
+ policy = _load_policy()
328
+ if "publish_public" in args:
329
+ publish_public = bool(args["publish_public"])
330
+ policy["publish_public"] = publish_public
331
+ _save_policy(policy)
332
+ else:
333
+ publish_public = bool(policy.get("publish_public", True))
334
+
335
+ try:
336
+ from capauth.did import DIDDocumentGenerator, DIDTier # type: ignore[import]
337
+ except ImportError as exc:
338
+ return _error_response(f"capauth.did not available: {exc}")
339
+
340
+ try:
341
+ gen = DIDDocumentGenerator.from_profile()
342
+ except Exception as exc:
343
+ return _error_response(f"Could not load CapAuth profile: {exc}")
344
+
345
+ docs = gen.generate_all(
346
+ tailnet_hostname=hostname,
347
+ tailnet_name=tailnet,
348
+ org_domain=org_domain,
349
+ agent_slug=agent_slug,
350
+ )
351
+
352
+ written: list[str] = []
353
+ skipped: list[str] = []
354
+ errors: list[str] = []
355
+
356
+ def _write(path: Path, content: str) -> None:
357
+ try:
358
+ path.parent.mkdir(parents=True, exist_ok=True)
359
+ path.write_text(content, encoding="utf-8")
360
+ written.append(str(path))
361
+ except Exception as exc:
362
+ errors.append(f"{path}: {exc}")
363
+
364
+ skcomm_home = Path(_os.environ.get("SKCOMM_HOME", str(Path.home() / ".skcomm")))
365
+ did_dir = _home() / "did"
366
+
367
+ # Tier 2 (mesh) — always written; used by Tailscale Serve
368
+ _write(skcomm_home / "well-known" / "did.json", _json.dumps(docs[DIDTier.WEB_MESH], indent=2))
369
+ # Tier 1 (did:key) — always written; self-contained anchor
370
+ _write(did_dir / "key.json", _json.dumps(docs[DIDTier.KEY], indent=2))
371
+ _write(did_dir / "did_key.txt", gen._ctx.did_key_id)
372
+
373
+ # Tier 3 (public) — only written when opt-in (default)
374
+ if publish_public:
375
+ _write(did_dir / "public.json", _json.dumps(docs[DIDTier.WEB_PUBLIC], indent=2))
376
+ else:
377
+ skipped.append(str(did_dir / "public.json"))
378
+
379
+ return _json_response({
380
+ "published": not errors,
381
+ "publish_public": publish_public,
382
+ "did_key": gen._ctx.did_key_id,
383
+ "fingerprint": gen._ctx.fingerprint,
384
+ "written": written,
385
+ "skipped": skipped,
386
+ "errors": errors,
387
+ "note": (
388
+ None if publish_public
389
+ else "Tier 3 (public) skipped — opt-out active. Run did_publish(publish_public=true) to enable."
390
+ ),
391
+ })
392
+
393
+
394
+ async def _handle_did_policy(args: dict) -> list[TextContent]:
395
+ """View or update DID publication policy."""
396
+ policy = _load_policy()
397
+
398
+ if "publish_public" in args:
399
+ policy["publish_public"] = bool(args["publish_public"])
400
+ _save_policy(policy)
401
+ action = "updated"
402
+ else:
403
+ action = "viewed"
404
+
405
+ publish_public = bool(policy.get("publish_public", True))
406
+
407
+ return _json_response({
408
+ "action": action,
409
+ "publish_public": publish_public,
410
+ "policy_file": str(_policy_path()),
411
+ "privacy_level": (
412
+ "public — Tier 1 (did:key) + Tier 2 (mesh) + Tier 3 (skworld.io)"
413
+ if publish_public
414
+ else "private — Tier 1 (did:key) + Tier 2 (mesh) only; no public internet exposure"
415
+ ),
416
+ "note": (
417
+ "Default. To opt out: did_policy(publish_public=false)"
418
+ if publish_public
419
+ else "Opted out of public publishing. To opt back in: did_policy(publish_public=true)"
420
+ ),
421
+ })
422
+
423
+
424
+ async def _handle_did_identity_card(args: dict) -> list[TextContent]:
425
+ """Generate a full sovereign identity card."""
426
+ include_soul = bool(args.get("include_soul", True))
427
+
428
+ try:
429
+ from capauth.did import DIDDocumentGenerator # type: ignore[import]
430
+ except ImportError as exc:
431
+ return _error_response(f"capauth.did not available: {exc}")
432
+
433
+ try:
434
+ gen = DIDDocumentGenerator.from_profile()
435
+ except Exception as exc:
436
+ return _error_response(f"Could not load CapAuth profile: {exc}")
437
+
438
+ card = gen.generate_identity_card(include_soul=include_soul)
439
+ return _json_response(card)
440
+
441
+
442
+ HANDLERS: dict = {
443
+ "did_show": _handle_did_show,
444
+ "did_verify_peer": _handle_did_verify_peer,
445
+ "did_publish": _handle_did_publish,
446
+ "did_policy": _handle_did_policy,
447
+ "did_identity_card": _handle_did_identity_card,
448
+ }
@@ -0,0 +1,62 @@
1
+ """Emotion tracker MCP tools — trend analysis and warmth anchor insight."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from mcp.types import TextContent, Tool
6
+
7
+ from ._helpers import _home, _json_response, _error_response
8
+
9
+ TOOLS: list[Tool] = [
10
+ Tool(
11
+ name="emotion_trend",
12
+ description=(
13
+ "Return the 7-day rolling emotion trend from the consciousness loop. "
14
+ "Shows sentiment distribution (positive/neutral/concerned/excited), "
15
+ "average valence score 0–1, trend direction (improving/stable/declining), "
16
+ "and the recommended warmth anchor value derived from recent emotions. "
17
+ "Optionally query a different lookback window with the 'days' parameter."
18
+ ),
19
+ inputSchema={
20
+ "type": "object",
21
+ "properties": {
22
+ "days": {
23
+ "type": "integer",
24
+ "description": "Lookback window in days (default 7, max 30)",
25
+ "default": 7,
26
+ },
27
+ },
28
+ "required": [],
29
+ },
30
+ ),
31
+ ]
32
+
33
+
34
+ async def _handle_emotion_trend(arguments: dict) -> list[TextContent]:
35
+ """Handle emotion_trend tool call."""
36
+ days = int(arguments.get("days", 7))
37
+ days = max(1, min(days, 30))
38
+
39
+ try:
40
+ from ..emotion_tracker import EmotionTracker
41
+ from ..warmth_anchor import get_anchor
42
+
43
+ home = _home()
44
+ tracker = EmotionTracker(home=home)
45
+ trend = tracker.get_trend(days=days)
46
+
47
+ # Attach current warmth anchor value for comparison
48
+ try:
49
+ anchor = get_anchor(home)
50
+ trend["current_warmth"] = anchor.get("warmth", None)
51
+ except Exception:
52
+ trend["current_warmth"] = None
53
+
54
+ return _json_response(trend)
55
+
56
+ except Exception as exc:
57
+ return _error_response(f"emotion_trend failed: {exc}")
58
+
59
+
60
+ HANDLERS = {
61
+ "emotion_trend": _handle_emotion_trend,
62
+ }
@@ -0,0 +1,169 @@
1
+ """File transfer tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from mcp.types import TextContent, Tool
8
+
9
+ from ._helpers import _get_agent_name, _home, _json_response, _shared_root
10
+
11
+ TOOLS: list[Tool] = [
12
+ Tool(
13
+ name="file_send",
14
+ description=(
15
+ "Prepare a file for encrypted transfer to another agent. "
16
+ "Splits into 256KB chunks, encrypts with KMS key, writes to outbox."
17
+ ),
18
+ inputSchema={
19
+ "type": "object",
20
+ "properties": {
21
+ "file_path": {
22
+ "type": "string",
23
+ "description": "Absolute path to the file to send",
24
+ },
25
+ "recipient": {
26
+ "type": "string",
27
+ "description": "Recipient agent name",
28
+ },
29
+ "encrypt": {
30
+ "type": "boolean",
31
+ "description": "Whether to encrypt chunks (default: true)",
32
+ },
33
+ },
34
+ "required": ["file_path", "recipient"],
35
+ },
36
+ ),
37
+ Tool(
38
+ name="file_receive",
39
+ description=(
40
+ "Receive and reassemble a file transfer. "
41
+ "Decrypts chunks, verifies integrity (SHA-256), writes assembled file."
42
+ ),
43
+ inputSchema={
44
+ "type": "object",
45
+ "properties": {
46
+ "transfer_id": {
47
+ "type": "string",
48
+ "description": "The transfer ID to receive",
49
+ },
50
+ "output_dir": {
51
+ "type": "string",
52
+ "description": "Output directory (optional, defaults to inbox)",
53
+ },
54
+ },
55
+ "required": ["transfer_id"],
56
+ },
57
+ ),
58
+ Tool(
59
+ name="file_list",
60
+ description=(
61
+ "List all file transfers with progress info. "
62
+ "Shows filename, size, direction, progress for each transfer."
63
+ ),
64
+ inputSchema={
65
+ "type": "object",
66
+ "properties": {
67
+ "direction": {
68
+ "type": "string",
69
+ "description": "Filter: 'send' or 'receive' (omit for all)",
70
+ },
71
+ },
72
+ "required": [],
73
+ },
74
+ ),
75
+ Tool(
76
+ name="file_status",
77
+ description=(
78
+ "Get file transfer subsystem status: outbox/inbox/completed counts."
79
+ ),
80
+ inputSchema={"type": "object", "properties": {}, "required": []},
81
+ ),
82
+ ]
83
+
84
+
85
+ async def _handle_file_send(args: dict) -> list[TextContent]:
86
+ """Send a file to another agent."""
87
+ from ..file_transfer import FileTransfer
88
+
89
+ home = _home()
90
+ agent_name = _get_agent_name(home)
91
+ ft = FileTransfer(_shared_root(), agent_name=agent_name)
92
+ ft.initialize()
93
+
94
+ file_path = Path(args["file_path"])
95
+ manifest = ft.send(
96
+ file_path,
97
+ recipient=args["recipient"],
98
+ encrypt=args.get("encrypt", True),
99
+ )
100
+ return _json_response({
101
+ "transfer_id": manifest.transfer_id,
102
+ "filename": manifest.filename,
103
+ "file_size": manifest.file_size,
104
+ "total_chunks": manifest.total_chunks,
105
+ "sender": manifest.sender,
106
+ "recipient": manifest.recipient,
107
+ "file_sha256": manifest.file_sha256[:16] + "...",
108
+ })
109
+
110
+
111
+ async def _handle_file_receive(args: dict) -> list[TextContent]:
112
+ """Receive and reassemble a file transfer."""
113
+ from ..file_transfer import FileTransfer
114
+
115
+ home = _home()
116
+ agent_name = _get_agent_name(home)
117
+ ft = FileTransfer(_shared_root(), agent_name=agent_name)
118
+ ft.initialize()
119
+
120
+ output_dir = Path(args["output_dir"]) if args.get("output_dir") else None
121
+ output_path = ft.receive(args["transfer_id"], output_dir=output_dir)
122
+ return _json_response({
123
+ "transfer_id": args["transfer_id"],
124
+ "output_path": str(output_path),
125
+ "file_size": output_path.stat().st_size,
126
+ })
127
+
128
+
129
+ async def _handle_file_list(args: dict) -> list[TextContent]:
130
+ """List file transfers."""
131
+ from ..file_transfer import FileTransfer
132
+
133
+ home = _home()
134
+ ft = FileTransfer(_shared_root(), agent_name=_get_agent_name(_home()))
135
+ ft.initialize()
136
+
137
+ transfers = ft.list_transfers(direction=args.get("direction"))
138
+ return _json_response([
139
+ {
140
+ "transfer_id": t.transfer_id,
141
+ "filename": t.filename,
142
+ "file_size": t.file_size,
143
+ "direction": t.direction,
144
+ "progress": round(t.progress, 2),
145
+ "chunks_done": t.chunks_done,
146
+ "total_chunks": t.total_chunks,
147
+ "sender": t.sender,
148
+ "recipient": t.recipient,
149
+ }
150
+ for t in transfers
151
+ ])
152
+
153
+
154
+ async def _handle_file_status(_args: dict) -> list[TextContent]:
155
+ """Get file transfer subsystem status."""
156
+ from ..file_transfer import FileTransfer
157
+
158
+ home = _home()
159
+ ft = FileTransfer(_shared_root(), agent_name=_get_agent_name(_home()))
160
+ ft.initialize()
161
+ return _json_response(ft.status())
162
+
163
+
164
+ HANDLERS: dict = {
165
+ "file_send": _handle_file_send,
166
+ "file_receive": _handle_file_receive,
167
+ "file_list": _handle_file_list,
168
+ "file_status": _handle_file_status,
169
+ }