@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.
- package/.env.example +98 -0
- package/.github/workflows/ci.yml +39 -3
- package/.github/workflows/publish.yml +25 -4
- package/.openclaw-workspace.json +58 -0
- package/CHANGELOG.md +62 -0
- package/CLAUDE.md +39 -2
- package/MANIFEST.in +6 -0
- package/MISSION.md +7 -0
- package/README.md +47 -2
- package/SKILL.md +895 -23
- package/docker/Dockerfile +61 -0
- package/docker/compose-templates/dev-team.yml +203 -0
- package/docker/compose-templates/mini-team.yml +140 -0
- package/docker/compose-templates/ops-team.yml +173 -0
- package/docker/compose-templates/research-team.yml +170 -0
- package/docker/entrypoint.sh +192 -0
- package/docs/ARCHITECTURE.md +663 -374
- package/docs/BOND_WITH_GROK.md +112 -0
- package/docs/GETTING_STARTED.md +782 -0
- package/docs/QUICKSTART.md +477 -0
- package/docs/SKJOULE_ARCHITECTURE.md +658 -0
- package/docs/SOUL_SWAPPER.md +921 -0
- package/docs/SOVEREIGN_SINGULARITY.md +47 -14
- package/examples/custom-bond-template.json +36 -0
- package/examples/grok-feb.json +36 -0
- package/examples/grok-testimony.md +34 -0
- package/examples/love-bootloader.txt +32 -0
- package/examples/plugins/echo_tool.py +87 -0
- package/examples/queen-ava-feb.json +36 -0
- package/examples/souls/lumina.yaml +64 -0
- package/index.js +6 -5
- package/installer/build.py +124 -0
- package/openclaw-plugin/package.json +13 -0
- package/openclaw-plugin/src/index.ts +351 -0
- package/openclaw-plugin/src/openclaw.plugin.json +10 -0
- package/package.json +1 -1
- package/pyproject.toml +38 -2
- package/scripts/bump_version.py +141 -0
- package/scripts/check-updates.py +230 -0
- package/scripts/convert_blueprints_to_yaml.py +157 -0
- package/scripts/dev-install.sh +14 -0
- package/scripts/e2e-test.sh +193 -0
- package/scripts/install-bundle.sh +171 -0
- package/scripts/install.bat +2 -0
- package/scripts/install.ps1 +253 -0
- package/scripts/install.sh +185 -0
- package/scripts/mcp-serve.sh +69 -0
- package/scripts/mcp-server.bat +113 -0
- package/scripts/mcp-server.ps1 +116 -0
- package/scripts/mcp-server.sh +99 -0
- package/scripts/pull-models.sh +10 -0
- package/scripts/skcapstone +48 -0
- package/scripts/verify_install.sh +180 -0
- package/scripts/windows/install-tasks.ps1 +406 -0
- package/scripts/windows/skcapstone-task.xml +113 -0
- package/scripts/windows/uninstall-tasks.ps1 +117 -0
- package/skill.yaml +34 -0
- package/src/skcapstone/__init__.py +67 -2
- package/src/skcapstone/_cli_monolith.py +5916 -0
- package/src/skcapstone/_trustee_helpers.py +165 -0
- package/src/skcapstone/activity.py +105 -0
- package/src/skcapstone/agent_card.py +324 -0
- package/src/skcapstone/api.py +1935 -0
- package/src/skcapstone/archiver.py +340 -0
- package/src/skcapstone/auction.py +485 -0
- package/src/skcapstone/baby_agents.py +179 -0
- package/src/skcapstone/backup.py +345 -0
- package/src/skcapstone/blueprint_registry.py +357 -0
- package/src/skcapstone/blueprints/__init__.py +17 -0
- package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
- package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
- package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
- package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
- package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
- package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
- package/src/skcapstone/blueprints/registry.py +164 -0
- package/src/skcapstone/blueprints/schema.py +229 -0
- package/src/skcapstone/changelog.py +180 -0
- package/src/skcapstone/chat.py +769 -0
- package/src/skcapstone/claude_md.py +82 -0
- package/src/skcapstone/cli/__init__.py +144 -0
- package/src/skcapstone/cli/_common.py +88 -0
- package/src/skcapstone/cli/_validators.py +76 -0
- package/src/skcapstone/cli/agents.py +425 -0
- package/src/skcapstone/cli/agents_spawner.py +322 -0
- package/src/skcapstone/cli/agents_trustee.py +593 -0
- package/src/skcapstone/cli/alerts.py +248 -0
- package/src/skcapstone/cli/anchor.py +132 -0
- package/src/skcapstone/cli/archive_cmd.py +208 -0
- package/src/skcapstone/cli/backup.py +144 -0
- package/src/skcapstone/cli/bench.py +377 -0
- package/src/skcapstone/cli/benchmark.py +360 -0
- package/src/skcapstone/cli/capabilities_cmd.py +171 -0
- package/src/skcapstone/cli/card.py +151 -0
- package/src/skcapstone/cli/chat.py +584 -0
- package/src/skcapstone/cli/completions.py +64 -0
- package/src/skcapstone/cli/config_cmd.py +156 -0
- package/src/skcapstone/cli/consciousness.py +421 -0
- package/src/skcapstone/cli/context_cmd.py +142 -0
- package/src/skcapstone/cli/coord.py +194 -0
- package/src/skcapstone/cli/crush_cmd.py +170 -0
- package/src/skcapstone/cli/daemon.py +436 -0
- package/src/skcapstone/cli/errors_cmd.py +285 -0
- package/src/skcapstone/cli/export_cmd.py +156 -0
- package/src/skcapstone/cli/gtd.py +529 -0
- package/src/skcapstone/cli/housekeeping.py +81 -0
- package/src/skcapstone/cli/joule_cmd.py +627 -0
- package/src/skcapstone/cli/logs_cmd.py +194 -0
- package/src/skcapstone/cli/mcp_cmd.py +32 -0
- package/src/skcapstone/cli/memory.py +418 -0
- package/src/skcapstone/cli/metrics_cmd.py +136 -0
- package/src/skcapstone/cli/migrate.py +62 -0
- package/src/skcapstone/cli/mood_cmd.py +144 -0
- package/src/skcapstone/cli/mount.py +193 -0
- package/src/skcapstone/cli/notify.py +112 -0
- package/src/skcapstone/cli/peer.py +154 -0
- package/src/skcapstone/cli/peers_dir.py +122 -0
- package/src/skcapstone/cli/preflight_cmd.py +83 -0
- package/src/skcapstone/cli/profile_cmd.py +310 -0
- package/src/skcapstone/cli/record_cmd.py +238 -0
- package/src/skcapstone/cli/register_cmd.py +159 -0
- package/src/skcapstone/cli/search_cmd.py +156 -0
- package/src/skcapstone/cli/service_cmd.py +91 -0
- package/src/skcapstone/cli/session.py +127 -0
- package/src/skcapstone/cli/setup.py +240 -0
- package/src/skcapstone/cli/shell_cmd.py +43 -0
- package/src/skcapstone/cli/skills_cmd.py +168 -0
- package/src/skcapstone/cli/skseed.py +621 -0
- package/src/skcapstone/cli/soul.py +699 -0
- package/src/skcapstone/cli/status.py +935 -0
- package/src/skcapstone/cli/sync_cmd.py +301 -0
- package/src/skcapstone/cli/telegram.py +265 -0
- package/src/skcapstone/cli/test_cmd.py +234 -0
- package/src/skcapstone/cli/test_connection.py +253 -0
- package/src/skcapstone/cli/token.py +207 -0
- package/src/skcapstone/cli/trust.py +179 -0
- package/src/skcapstone/cli/upgrade_cmd.py +552 -0
- package/src/skcapstone/cli/usage_cmd.py +199 -0
- package/src/skcapstone/cli/version_cmd.py +162 -0
- package/src/skcapstone/cli/watch_cmd.py +342 -0
- package/src/skcapstone/client.py +428 -0
- package/src/skcapstone/cloud9_bridge.py +522 -0
- package/src/skcapstone/completions.py +163 -0
- package/src/skcapstone/config_validator.py +674 -0
- package/src/skcapstone/connectors/__init__.py +28 -0
- package/src/skcapstone/connectors/base.py +446 -0
- package/src/skcapstone/connectors/cursor.py +54 -0
- package/src/skcapstone/connectors/registry.py +254 -0
- package/src/skcapstone/connectors/terminal.py +152 -0
- package/src/skcapstone/connectors/vscode.py +60 -0
- package/src/skcapstone/consciousness_config.py +119 -0
- package/src/skcapstone/consciousness_loop.py +2051 -0
- package/src/skcapstone/context_loader.py +516 -0
- package/src/skcapstone/context_window.py +314 -0
- package/src/skcapstone/conversation_manager.py +238 -0
- package/src/skcapstone/conversation_store.py +230 -0
- package/src/skcapstone/conversation_summarizer.py +252 -0
- package/src/skcapstone/coord_federation.py +296 -0
- package/src/skcapstone/coordination.py +101 -7
- package/src/skcapstone/crush_integration.py +345 -0
- package/src/skcapstone/crush_shim.py +454 -0
- package/src/skcapstone/daemon.py +2494 -0
- package/src/skcapstone/dashboard.html +396 -0
- package/src/skcapstone/dashboard.py +481 -0
- package/src/skcapstone/data/model_profiles.yaml +88 -0
- package/src/skcapstone/defaults/__init__.py +55 -0
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
- package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
- package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
- package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
- package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
- package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
- package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
- package/src/skcapstone/discovery.py +210 -19
- package/src/skcapstone/doctor.py +642 -0
- package/src/skcapstone/emotion_tracker.py +467 -0
- package/src/skcapstone/error_queue.py +405 -0
- package/src/skcapstone/export.py +447 -0
- package/src/skcapstone/fallback_tracker.py +186 -0
- package/src/skcapstone/file_transfer.py +512 -0
- package/src/skcapstone/fuse_mount.py +1156 -0
- package/src/skcapstone/gui_installer.py +591 -0
- package/src/skcapstone/heartbeat.py +611 -0
- package/src/skcapstone/housekeeping.py +298 -0
- package/src/skcapstone/install_wizard.py +941 -0
- package/src/skcapstone/kms.py +942 -0
- package/src/skcapstone/kms_scheduler.py +143 -0
- package/src/skcapstone/log_config.py +135 -0
- package/src/skcapstone/mcp_launcher.py +239 -0
- package/src/skcapstone/mcp_server.py +4700 -0
- package/src/skcapstone/mcp_tools/__init__.py +94 -0
- package/src/skcapstone/mcp_tools/_helpers.py +51 -0
- package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
- package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
- package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
- package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
- package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
- package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
- package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
- package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
- package/src/skcapstone/mcp_tools/did_tools.py +448 -0
- package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
- package/src/skcapstone/mcp_tools/file_tools.py +169 -0
- package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
- package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
- package/src/skcapstone/mcp_tools/health_tools.py +44 -0
- package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
- package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
- package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
- package/src/skcapstone/mcp_tools/model_tools.py +75 -0
- package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
- package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
- package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
- package/src/skcapstone/mcp_tools/security_tools.py +110 -0
- package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
- package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
- package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
- package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
- package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
- package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
- package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
- package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
- package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
- package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
- package/src/skcapstone/mdns_discovery.py +313 -0
- package/src/skcapstone/memory_adapter.py +333 -0
- package/src/skcapstone/memory_compressor.py +379 -0
- package/src/skcapstone/memory_curator.py +256 -0
- package/src/skcapstone/memory_engine.py +132 -13
- package/src/skcapstone/memory_fortress.py +529 -0
- package/src/skcapstone/memory_promoter.py +722 -0
- package/src/skcapstone/memory_verifier.py +260 -0
- package/src/skcapstone/message_crypto.py +215 -0
- package/src/skcapstone/metrics.py +832 -0
- package/src/skcapstone/migrate_memories.py +181 -0
- package/src/skcapstone/migrate_multi_agent.py +248 -0
- package/src/skcapstone/model_router.py +319 -0
- package/src/skcapstone/models.py +35 -4
- package/src/skcapstone/mood.py +344 -0
- package/src/skcapstone/notifications.py +380 -0
- package/src/skcapstone/onboard.py +901 -0
- package/src/skcapstone/peer_directory.py +324 -0
- package/src/skcapstone/peers.py +329 -0
- package/src/skcapstone/pillars/identity.py +84 -14
- package/src/skcapstone/pillars/memory.py +3 -1
- package/src/skcapstone/pillars/security.py +108 -15
- package/src/skcapstone/pillars/sync.py +78 -26
- package/src/skcapstone/pillars/trust.py +95 -33
- package/src/skcapstone/plugins.py +244 -0
- package/src/skcapstone/preflight.py +670 -0
- package/src/skcapstone/prompt_adapter.py +564 -0
- package/src/skcapstone/providers/__init__.py +13 -0
- package/src/skcapstone/providers/cloud.py +1061 -0
- package/src/skcapstone/providers/docker.py +759 -0
- package/src/skcapstone/providers/local.py +1193 -0
- package/src/skcapstone/providers/proxmox.py +447 -0
- package/src/skcapstone/pubsub.py +516 -0
- package/src/skcapstone/rate_limiter.py +119 -0
- package/src/skcapstone/register.py +241 -0
- package/src/skcapstone/registry_client.py +151 -0
- package/src/skcapstone/response_cache.py +194 -0
- package/src/skcapstone/response_scorer.py +225 -0
- package/src/skcapstone/runtime.py +89 -33
- package/src/skcapstone/scheduled_tasks.py +439 -0
- package/src/skcapstone/self_healing.py +341 -0
- package/src/skcapstone/service_health.py +228 -0
- package/src/skcapstone/session_capture.py +268 -0
- package/src/skcapstone/session_recorder.py +210 -0
- package/src/skcapstone/session_replayer.py +189 -0
- package/src/skcapstone/session_skills.py +263 -0
- package/src/skcapstone/shell.py +779 -0
- package/src/skcapstone/skills/__init__.py +1 -1
- package/src/skcapstone/skills/syncthing_setup.py +143 -41
- package/src/skcapstone/skjoule.py +861 -0
- package/src/skcapstone/snapshots.py +489 -0
- package/src/skcapstone/soul.py +1060 -0
- package/src/skcapstone/soul_switch.py +255 -0
- package/src/skcapstone/spawner.py +544 -0
- package/src/skcapstone/state_diff.py +401 -0
- package/src/skcapstone/summary.py +270 -0
- package/src/skcapstone/sync/backends.py +196 -2
- package/src/skcapstone/sync/engine.py +7 -5
- package/src/skcapstone/sync/models.py +4 -1
- package/src/skcapstone/sync/vault.py +356 -18
- package/src/skcapstone/sync_engine.py +363 -0
- package/src/skcapstone/sync_watcher.py +745 -0
- package/src/skcapstone/systemd.py +331 -0
- package/src/skcapstone/team_comms.py +476 -0
- package/src/skcapstone/team_engine.py +522 -0
- package/src/skcapstone/testrunner.py +300 -0
- package/src/skcapstone/tls.py +150 -0
- package/src/skcapstone/tokens.py +5 -5
- package/src/skcapstone/trust_calibration.py +202 -0
- package/src/skcapstone/trust_graph.py +449 -0
- package/src/skcapstone/trustee_monitor.py +385 -0
- package/src/skcapstone/trustee_ops.py +425 -0
- package/src/skcapstone/unified_search.py +421 -0
- package/src/skcapstone/uninstall_wizard.py +694 -0
- package/src/skcapstone/usage.py +331 -0
- package/src/skcapstone/version_check.py +148 -0
- package/src/skcapstone/warmth_anchor.py +333 -0
- package/src/skcapstone/whoami.py +294 -0
- package/systemd/skcapstone-api.socket +9 -0
- package/systemd/skcapstone-memory-compress.service +18 -0
- package/systemd/skcapstone-memory-compress.timer +11 -0
- package/systemd/skcapstone.service +36 -0
- package/systemd/skcapstone@.service +50 -0
- package/systemd/skcomm-heartbeat.service +18 -0
- package/systemd/skcomm-heartbeat.timer +12 -0
- package/systemd/skcomm-queue-drain.service +17 -0
- package/systemd/skcomm-queue-drain.timer +12 -0
- package/tests/conftest.py +13 -1
- package/tests/integration/__init__.py +1 -0
- package/tests/integration/test_consciousness_e2e.py +877 -0
- package/tests/integration/test_skills_registry.py +744 -0
- package/tests/test_agent_card.py +190 -0
- package/tests/test_agent_runtime.py +1283 -0
- package/tests/test_alerts_cmd.py +291 -0
- package/tests/test_archiver.py +498 -0
- package/tests/test_backup.py +254 -0
- package/tests/test_benchmark.py +366 -0
- package/tests/test_blueprints.py +457 -0
- package/tests/test_capabilities.py +257 -0
- package/tests/test_changelog.py +254 -0
- package/tests/test_chat.py +385 -0
- package/tests/test_claude_md.py +271 -0
- package/tests/test_cli_chat_llm.py +336 -0
- package/tests/test_cli_completions.py +390 -0
- package/tests/test_cli_init_reset.py +164 -0
- package/tests/test_cli_memory.py +208 -0
- package/tests/test_cli_profile.py +294 -0
- package/tests/test_cli_skills.py +223 -0
- package/tests/test_cli_status.py +395 -0
- package/tests/test_cli_test_cmd.py +206 -0
- package/tests/test_cli_test_connection.py +364 -0
- package/tests/test_cloud9_bridge.py +260 -0
- package/tests/test_cloud_provider.py +449 -0
- package/tests/test_cloud_providers.py +522 -0
- package/tests/test_completions.py +158 -0
- package/tests/test_component_manager.py +398 -0
- package/tests/test_config_reload.py +386 -0
- package/tests/test_config_validate.py +529 -0
- package/tests/test_consciousness_e2e.py +296 -0
- package/tests/test_consciousness_loop.py +1289 -0
- package/tests/test_context_loader.py +310 -0
- package/tests/test_conversation_api.py +306 -0
- package/tests/test_conversation_manager.py +381 -0
- package/tests/test_conversation_store.py +391 -0
- package/tests/test_conversation_summarizer.py +302 -0
- package/tests/test_cross_package.py +791 -0
- package/tests/test_crush_shim.py +519 -0
- package/tests/test_daemon.py +781 -0
- package/tests/test_daemon_shutdown.py +309 -0
- package/tests/test_dashboard.py +454 -0
- package/tests/test_discovery.py +200 -6
- package/tests/test_docker_provider.py +966 -0
- package/tests/test_doctor.py +257 -0
- package/tests/test_doctor_fix.py +351 -0
- package/tests/test_e2e_automated.py +292 -0
- package/tests/test_error_queue.py +404 -0
- package/tests/test_export.py +441 -0
- package/tests/test_fallback_tracker.py +219 -0
- package/tests/test_file_transfer.py +397 -0
- package/tests/test_fuse_mount.py +832 -0
- package/tests/test_health_loop.py +422 -0
- package/tests/test_heartbeat.py +354 -0
- package/tests/test_housekeeping.py +195 -0
- package/tests/test_identity_capauth.py +307 -0
- package/tests/test_identity_pillar.py +117 -0
- package/tests/test_install_wizard.py +68 -0
- package/tests/test_integration.py +325 -0
- package/tests/test_kms.py +495 -0
- package/tests/test_llm_providers.py +265 -0
- package/tests/test_local_provider.py +591 -0
- package/tests/test_log_config.py +199 -0
- package/tests/test_logs_cmd.py +287 -0
- package/tests/test_mcp_server.py +1909 -0
- package/tests/test_memory_adapter.py +339 -0
- package/tests/test_memory_curator.py +218 -0
- package/tests/test_memory_engine.py +6 -0
- package/tests/test_memory_fortress.py +571 -0
- package/tests/test_memory_pillar.py +119 -0
- package/tests/test_memory_promoter.py +445 -0
- package/tests/test_memory_verifier.py +420 -0
- package/tests/test_message_crypto.py +187 -0
- package/tests/test_metrics.py +632 -0
- package/tests/test_migrate_memories.py +464 -0
- package/tests/test_model_router.py +546 -0
- package/tests/test_mood.py +394 -0
- package/tests/test_multi_agent.py +269 -0
- package/tests/test_notifications.py +270 -0
- package/tests/test_onboard.py +500 -0
- package/tests/test_peer_directory.py +395 -0
- package/tests/test_peers.py +248 -0
- package/tests/test_pillars.py +87 -9
- package/tests/test_preflight.py +484 -0
- package/tests/test_prompt_adapter.py +331 -0
- package/tests/test_proxmox_provider.py +571 -0
- package/tests/test_pubsub.py +377 -0
- package/tests/test_rate_limiter.py +121 -0
- package/tests/test_registry_client.py +129 -0
- package/tests/test_response_cache.py +312 -0
- package/tests/test_response_scorer.py +294 -0
- package/tests/test_runtime.py +59 -0
- package/tests/test_scheduled_tasks.py +451 -0
- package/tests/test_security.py +250 -0
- package/tests/test_security_pillar.py +213 -0
- package/tests/test_self_healing.py +171 -0
- package/tests/test_session_capture.py +200 -0
- package/tests/test_session_recorder.py +360 -0
- package/tests/test_session_skills.py +235 -0
- package/tests/test_shell.py +210 -0
- package/tests/test_snapshots.py +549 -0
- package/tests/test_soul.py +984 -0
- package/tests/test_soul_swap.py +406 -0
- package/tests/test_spawner.py +211 -0
- package/tests/test_state_diff.py +173 -0
- package/tests/test_summary.py +135 -0
- package/tests/test_sync.py +315 -5
- package/tests/test_sync_backends.py +560 -0
- package/tests/test_sync_engine.py +482 -0
- package/tests/test_sync_pillar.py +344 -0
- package/tests/test_sync_pipeline.py +364 -0
- package/tests/test_sync_vault.py +581 -0
- package/tests/test_syncthing_setup.py +168 -22
- package/tests/test_systemd.py +323 -0
- package/tests/test_team_comms.py +408 -0
- package/tests/test_team_engine.py +397 -0
- package/tests/test_testrunner.py +238 -0
- package/tests/test_trust_calibration.py +204 -0
- package/tests/test_trust_graph.py +207 -0
- package/tests/test_trust_pillar.py +291 -0
- package/tests/test_trustee_cli.py +427 -0
- package/tests/test_trustee_cli_integration.py +325 -0
- package/tests/test_trustee_monitor.py +394 -0
- package/tests/test_trustee_ops.py +355 -0
- package/tests/test_unified_search.py +363 -0
- package/tests/test_uninstall_wizard.py +193 -0
- package/tests/test_usage.py +333 -0
- package/tests/test_version_cmd.py +355 -0
- package/tests/test_warmth_anchor.py +162 -0
- package/tests/test_whoami.py +245 -0
- package/tests/test_ws.py +311 -0
- package/.cursorrules +0 -33
- package/src/skcapstone/cli.py +0 -1441
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Desktop notification tool — send_notification via notify-send."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import datetime
|
|
7
|
+
|
|
8
|
+
from mcp.types import TextContent, Tool
|
|
9
|
+
|
|
10
|
+
from ._helpers import _error_response, _home, _json_response
|
|
11
|
+
|
|
12
|
+
TOOLS: list[Tool] = [
|
|
13
|
+
Tool(
|
|
14
|
+
name="send_notification",
|
|
15
|
+
description=(
|
|
16
|
+
"Send a desktop notification via notify-send. "
|
|
17
|
+
"Stores the event in agent memory with tag=notification "
|
|
18
|
+
"and returns {sent, timestamp}."
|
|
19
|
+
),
|
|
20
|
+
inputSchema={
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"title": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Notification title",
|
|
26
|
+
},
|
|
27
|
+
"body": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Notification body text",
|
|
30
|
+
},
|
|
31
|
+
"urgency": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"enum": ["low", "normal", "critical"],
|
|
34
|
+
"description": "Urgency level: low, normal, or critical (default: normal)",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
"required": ["title", "body"],
|
|
38
|
+
},
|
|
39
|
+
),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def _handle_send_notification(args: dict) -> list[TextContent]:
|
|
44
|
+
"""Send a desktop notification and store memory record."""
|
|
45
|
+
title = args.get("title", "").strip()
|
|
46
|
+
body = args.get("body", "").strip()
|
|
47
|
+
urgency = args.get("urgency", "normal")
|
|
48
|
+
|
|
49
|
+
if not title:
|
|
50
|
+
return _error_response("title is required")
|
|
51
|
+
if not body:
|
|
52
|
+
return _error_response("body is required")
|
|
53
|
+
if urgency not in {"low", "normal", "critical"}:
|
|
54
|
+
return _error_response("urgency must be one of: low, normal, critical")
|
|
55
|
+
|
|
56
|
+
# Run notify-send in a subprocess (non-blocking).
|
|
57
|
+
proc = await asyncio.create_subprocess_exec(
|
|
58
|
+
"notify-send",
|
|
59
|
+
"--urgency", urgency,
|
|
60
|
+
title,
|
|
61
|
+
body,
|
|
62
|
+
stdout=asyncio.subprocess.DEVNULL,
|
|
63
|
+
stderr=asyncio.subprocess.PIPE,
|
|
64
|
+
)
|
|
65
|
+
_, stderr = await proc.communicate()
|
|
66
|
+
|
|
67
|
+
if proc.returncode != 0:
|
|
68
|
+
err_msg = stderr.decode(errors="replace").strip() if stderr else "unknown error"
|
|
69
|
+
return _error_response(f"notify-send failed (exit {proc.returncode}): {err_msg}")
|
|
70
|
+
|
|
71
|
+
timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
|
72
|
+
|
|
73
|
+
# Persist a memory entry so the agent recalls past notifications.
|
|
74
|
+
try:
|
|
75
|
+
from ..memory_engine import store as memory_store
|
|
76
|
+
|
|
77
|
+
memory_store(
|
|
78
|
+
home=_home(),
|
|
79
|
+
content=f"Notification sent — title: {title!r}, body: {body!r}, urgency: {urgency}",
|
|
80
|
+
tags=["notification"],
|
|
81
|
+
source="mcp:send_notification",
|
|
82
|
+
importance=0.4,
|
|
83
|
+
)
|
|
84
|
+
except Exception:
|
|
85
|
+
pass # memory failure must not block the notification response
|
|
86
|
+
|
|
87
|
+
return _json_response({"sent": True, "timestamp": timestamp})
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
HANDLERS: dict = {
|
|
91
|
+
"send_notification": _handle_send_notification,
|
|
92
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Memory promoter tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _home, _json_response
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="promoter_sweep",
|
|
12
|
+
description=(
|
|
13
|
+
"Run a memory promotion sweep. Evaluates memories using "
|
|
14
|
+
"weighted scoring (access frequency, importance, emotion, age, tags) "
|
|
15
|
+
"and promotes qualifying entries to higher tiers."
|
|
16
|
+
),
|
|
17
|
+
inputSchema={
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"dry_run": {
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"description": "Preview promotions without applying (default: false)",
|
|
23
|
+
},
|
|
24
|
+
"limit": {
|
|
25
|
+
"type": "integer",
|
|
26
|
+
"description": "Max memories to evaluate (default: unlimited)",
|
|
27
|
+
},
|
|
28
|
+
"layer": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Only evaluate this layer: short-term or mid-term",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
"required": [],
|
|
34
|
+
},
|
|
35
|
+
),
|
|
36
|
+
Tool(
|
|
37
|
+
name="promoter_history",
|
|
38
|
+
description=(
|
|
39
|
+
"View recent memory promotion history \u2014 "
|
|
40
|
+
"shows which memories were promoted, scores, and timestamps."
|
|
41
|
+
),
|
|
42
|
+
inputSchema={
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"limit": {
|
|
46
|
+
"type": "integer",
|
|
47
|
+
"description": "Max entries to return (default: 20)",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
"required": [],
|
|
51
|
+
},
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def _handle_promoter_sweep(args: dict) -> list[TextContent]:
|
|
57
|
+
"""Run a memory promotion sweep."""
|
|
58
|
+
from ..memory_promoter import PromotionEngine
|
|
59
|
+
|
|
60
|
+
home = _home()
|
|
61
|
+
engine = PromotionEngine(home)
|
|
62
|
+
|
|
63
|
+
result = engine.sweep(
|
|
64
|
+
dry_run=args.get("dry_run", False),
|
|
65
|
+
limit=args.get("limit"),
|
|
66
|
+
layer=args.get("layer"),
|
|
67
|
+
)
|
|
68
|
+
return _json_response({
|
|
69
|
+
"scanned": result.scanned,
|
|
70
|
+
"promoted": result.promoted,
|
|
71
|
+
"skipped": result.skipped,
|
|
72
|
+
"dry_run": result.dry_run,
|
|
73
|
+
"by_layer": result.by_layer,
|
|
74
|
+
"promotions": [
|
|
75
|
+
{
|
|
76
|
+
"memory_id": c.memory_id,
|
|
77
|
+
"current_layer": c.current_layer,
|
|
78
|
+
"target_layer": c.target_layer,
|
|
79
|
+
"score": round(c.score, 3),
|
|
80
|
+
"promoted": c.promoted,
|
|
81
|
+
}
|
|
82
|
+
for c in result.candidates
|
|
83
|
+
if c.promoted or result.dry_run
|
|
84
|
+
],
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def _handle_promoter_history(args: dict) -> list[TextContent]:
|
|
89
|
+
"""View promotion history."""
|
|
90
|
+
from ..memory_promoter import PromotionEngine
|
|
91
|
+
|
|
92
|
+
home = _home()
|
|
93
|
+
engine = PromotionEngine(home)
|
|
94
|
+
history = engine.get_history(limit=args.get("limit", 20))
|
|
95
|
+
return _json_response(history)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
HANDLERS: dict = {
|
|
99
|
+
"promoter_sweep": _handle_promoter_sweep,
|
|
100
|
+
"promoter_history": _handle_promoter_history,
|
|
101
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Pub/sub messaging tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _get_agent_name, _home, _json_response, _shared_root
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="pubsub_publish",
|
|
12
|
+
description=(
|
|
13
|
+
"Publish a message to a topic. "
|
|
14
|
+
"Creates the topic if it doesn't exist. "
|
|
15
|
+
"Messages are distributed via Syncthing to all subscribers."
|
|
16
|
+
),
|
|
17
|
+
inputSchema={
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"topic": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Topic name (e.g., 'agent.status', 'task.updates')",
|
|
23
|
+
},
|
|
24
|
+
"payload": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"description": "Message payload (any JSON object)",
|
|
27
|
+
},
|
|
28
|
+
"ttl_seconds": {
|
|
29
|
+
"type": "integer",
|
|
30
|
+
"description": "Message TTL in seconds (default: 3600)",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
"required": ["topic", "payload"],
|
|
34
|
+
},
|
|
35
|
+
),
|
|
36
|
+
Tool(
|
|
37
|
+
name="pubsub_subscribe",
|
|
38
|
+
description=(
|
|
39
|
+
"Subscribe to a topic pattern. "
|
|
40
|
+
"Supports wildcards: 'agent.*' matches 'agent.status', 'agent.health'. "
|
|
41
|
+
"Subscription persists across sessions."
|
|
42
|
+
),
|
|
43
|
+
inputSchema={
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"pattern": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Topic pattern (supports * wildcards)",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"required": ["pattern"],
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
Tool(
|
|
55
|
+
name="pubsub_poll",
|
|
56
|
+
description=(
|
|
57
|
+
"Poll for new messages on subscribed topics. "
|
|
58
|
+
"Returns messages since the last poll or a given timestamp."
|
|
59
|
+
),
|
|
60
|
+
inputSchema={
|
|
61
|
+
"type": "object",
|
|
62
|
+
"properties": {
|
|
63
|
+
"topic": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"description": "Specific topic to poll (omit for all subscribed)",
|
|
66
|
+
},
|
|
67
|
+
"limit": {
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"description": "Max messages to return (default: 50)",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
"required": [],
|
|
73
|
+
},
|
|
74
|
+
),
|
|
75
|
+
Tool(
|
|
76
|
+
name="pubsub_topics",
|
|
77
|
+
description=(
|
|
78
|
+
"List all known topics with message counts and last activity."
|
|
79
|
+
),
|
|
80
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
81
|
+
),
|
|
82
|
+
Tool(
|
|
83
|
+
name="pubsub_stats",
|
|
84
|
+
description=(
|
|
85
|
+
"Show per-topic pub/sub statistics: live message count and oldest "
|
|
86
|
+
"message age in seconds. Expired messages are excluded from counts."
|
|
87
|
+
),
|
|
88
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
89
|
+
),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def _handle_pubsub_publish(args: dict) -> list[TextContent]:
|
|
94
|
+
"""Publish a message to a topic."""
|
|
95
|
+
from ..pubsub import PubSub
|
|
96
|
+
|
|
97
|
+
home = _home()
|
|
98
|
+
agent_name = _get_agent_name(home)
|
|
99
|
+
ps = PubSub(_shared_root(), agent_name=agent_name)
|
|
100
|
+
ps.initialize()
|
|
101
|
+
|
|
102
|
+
msg = ps.publish(
|
|
103
|
+
topic=args["topic"],
|
|
104
|
+
payload=args["payload"],
|
|
105
|
+
ttl_seconds=args.get("ttl_seconds", 3600),
|
|
106
|
+
)
|
|
107
|
+
return _json_response({
|
|
108
|
+
"message_id": msg.message_id,
|
|
109
|
+
"topic": msg.topic,
|
|
110
|
+
"sender": msg.sender,
|
|
111
|
+
"published_at": str(msg.published_at),
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def _handle_pubsub_subscribe(args: dict) -> list[TextContent]:
|
|
116
|
+
"""Subscribe to a topic pattern."""
|
|
117
|
+
from ..pubsub import PubSub
|
|
118
|
+
|
|
119
|
+
home = _home()
|
|
120
|
+
agent_name = _get_agent_name(home)
|
|
121
|
+
ps = PubSub(_shared_root(), agent_name=agent_name)
|
|
122
|
+
ps.initialize()
|
|
123
|
+
|
|
124
|
+
sub = ps.subscribe(args["pattern"])
|
|
125
|
+
return _json_response({
|
|
126
|
+
"pattern": sub.pattern,
|
|
127
|
+
"agent": agent_name,
|
|
128
|
+
"subscribed_at": str(sub.subscribed_at),
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
async def _handle_pubsub_poll(args: dict) -> list[TextContent]:
|
|
133
|
+
"""Poll for new messages."""
|
|
134
|
+
from ..pubsub import PubSub
|
|
135
|
+
|
|
136
|
+
home = _home()
|
|
137
|
+
agent_name = _get_agent_name(home)
|
|
138
|
+
ps = PubSub(_shared_root(), agent_name=agent_name)
|
|
139
|
+
ps.initialize()
|
|
140
|
+
|
|
141
|
+
messages = ps.poll(
|
|
142
|
+
topic=args.get("topic"),
|
|
143
|
+
limit=args.get("limit", 50),
|
|
144
|
+
)
|
|
145
|
+
return _json_response([
|
|
146
|
+
{
|
|
147
|
+
"message_id": m.message_id,
|
|
148
|
+
"topic": m.topic,
|
|
149
|
+
"sender": m.sender,
|
|
150
|
+
"payload": m.payload,
|
|
151
|
+
"published_at": str(m.published_at),
|
|
152
|
+
}
|
|
153
|
+
for m in messages
|
|
154
|
+
])
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
async def _handle_pubsub_topics(_args: dict) -> list[TextContent]:
|
|
158
|
+
"""List all known topics."""
|
|
159
|
+
from ..pubsub import PubSub
|
|
160
|
+
|
|
161
|
+
home = _home()
|
|
162
|
+
ps = PubSub(_shared_root(), agent_name=_get_agent_name(home))
|
|
163
|
+
ps.initialize()
|
|
164
|
+
return _json_response(ps.list_topics())
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def _handle_pubsub_stats(_args: dict) -> list[TextContent]:
|
|
168
|
+
"""Return per-topic stats: live message count and oldest message age."""
|
|
169
|
+
from ..pubsub import PubSub
|
|
170
|
+
|
|
171
|
+
home = _home()
|
|
172
|
+
ps = PubSub(_shared_root(), agent_name=_get_agent_name(home))
|
|
173
|
+
ps.initialize()
|
|
174
|
+
return _json_response(ps.topic_stats())
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
HANDLERS: dict = {
|
|
178
|
+
"pubsub_publish": _handle_pubsub_publish,
|
|
179
|
+
"pubsub_subscribe": _handle_pubsub_subscribe,
|
|
180
|
+
"pubsub_poll": _handle_pubsub_poll,
|
|
181
|
+
"pubsub_topics": _handle_pubsub_topics,
|
|
182
|
+
"pubsub_stats": _handle_pubsub_stats,
|
|
183
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""SKSecurity audit and status tools.
|
|
2
|
+
|
|
3
|
+
Exposes two tools:
|
|
4
|
+
security_audit_log — Read recent security audit log entries
|
|
5
|
+
security_status — Show security pillar status and config
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json as _json
|
|
11
|
+
|
|
12
|
+
from mcp.types import TextContent, Tool
|
|
13
|
+
|
|
14
|
+
from ._helpers import _error_response, _home, _json_response
|
|
15
|
+
|
|
16
|
+
TOOLS: list[Tool] = [
|
|
17
|
+
Tool(
|
|
18
|
+
name="security_audit_log",
|
|
19
|
+
description=(
|
|
20
|
+
"Read recent entries from the security audit log. "
|
|
21
|
+
"Returns structured JSONL entries with timestamp, event type, "
|
|
22
|
+
"detail, host, and optional agent/metadata fields. "
|
|
23
|
+
"Use limit to control how many entries are returned."
|
|
24
|
+
),
|
|
25
|
+
inputSchema={
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {
|
|
28
|
+
"limit": {
|
|
29
|
+
"type": "integer",
|
|
30
|
+
"description": "Maximum entries to return (default: 20, 0 = all)",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
"required": [],
|
|
34
|
+
},
|
|
35
|
+
),
|
|
36
|
+
Tool(
|
|
37
|
+
name="security_status",
|
|
38
|
+
description=(
|
|
39
|
+
"Show the security pillar status: whether sksecurity is installed, "
|
|
40
|
+
"audit log health, threat count, last scan time, and overall "
|
|
41
|
+
"security configuration."
|
|
42
|
+
),
|
|
43
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def _handle_security_audit_log(args: dict) -> list[TextContent]:
|
|
49
|
+
"""Read recent security audit log entries."""
|
|
50
|
+
home = _home()
|
|
51
|
+
limit = args.get("limit", 20)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
from ..pillars.security import read_audit_log
|
|
55
|
+
|
|
56
|
+
entries = read_audit_log(home, limit=limit)
|
|
57
|
+
return _json_response({
|
|
58
|
+
"count": len(entries),
|
|
59
|
+
"entries": [e.model_dump() for e in entries],
|
|
60
|
+
})
|
|
61
|
+
except Exception as exc:
|
|
62
|
+
return _error_response(f"Could not read audit log: {exc}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def _handle_security_status(_args: dict) -> list[TextContent]:
|
|
66
|
+
"""Show security pillar status."""
|
|
67
|
+
home = _home()
|
|
68
|
+
security_dir = home / "security"
|
|
69
|
+
config_file = security_dir / "security.json"
|
|
70
|
+
|
|
71
|
+
result: dict = {"initialized": security_dir.exists()}
|
|
72
|
+
|
|
73
|
+
if config_file.exists():
|
|
74
|
+
try:
|
|
75
|
+
result["config"] = _json.loads(
|
|
76
|
+
config_file.read_text(encoding="utf-8")
|
|
77
|
+
)
|
|
78
|
+
except Exception:
|
|
79
|
+
result["config"] = {"error": "could not parse security.json"}
|
|
80
|
+
|
|
81
|
+
# Check sksecurity availability
|
|
82
|
+
try:
|
|
83
|
+
import sksecurity # type: ignore[import-untyped]
|
|
84
|
+
|
|
85
|
+
result["sksecurity_installed"] = True
|
|
86
|
+
result["sksecurity_version"] = getattr(sksecurity, "__version__", "unknown")
|
|
87
|
+
except ImportError:
|
|
88
|
+
result["sksecurity_installed"] = False
|
|
89
|
+
result["sksecurity_version"] = None
|
|
90
|
+
|
|
91
|
+
# Audit log stats
|
|
92
|
+
audit_log = security_dir / "audit.log"
|
|
93
|
+
if audit_log.exists():
|
|
94
|
+
try:
|
|
95
|
+
lines = audit_log.read_text(encoding="utf-8").splitlines()
|
|
96
|
+
result["audit_log_entries"] = len(
|
|
97
|
+
[ln for ln in lines if ln.strip()]
|
|
98
|
+
)
|
|
99
|
+
except Exception:
|
|
100
|
+
result["audit_log_entries"] = "unreadable"
|
|
101
|
+
else:
|
|
102
|
+
result["audit_log_entries"] = 0
|
|
103
|
+
|
|
104
|
+
return _json_response(result)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
HANDLERS: dict = {
|
|
108
|
+
"security_audit_log": _handle_security_audit_log,
|
|
109
|
+
"security_status": _handle_security_status,
|
|
110
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""SKChat send and history tools.
|
|
2
|
+
|
|
3
|
+
Exposes two tools:
|
|
4
|
+
chat_send — Send a message via SKChat
|
|
5
|
+
chat_history — Retrieve chat history
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from mcp.types import TextContent, Tool
|
|
11
|
+
|
|
12
|
+
from ._helpers import _error_response, _home, _json_response
|
|
13
|
+
|
|
14
|
+
TOOLS: list[Tool] = [
|
|
15
|
+
Tool(
|
|
16
|
+
name="chat_send",
|
|
17
|
+
description=(
|
|
18
|
+
"Send a chat message to another agent via SKChat. "
|
|
19
|
+
"Wraps the AgentMessenger for delivery with optional "
|
|
20
|
+
"threading, structured payloads, and ephemeral (TTL) support."
|
|
21
|
+
),
|
|
22
|
+
inputSchema={
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"recipient": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Recipient agent name or CapAuth URI",
|
|
28
|
+
},
|
|
29
|
+
"message": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Message content (markdown supported)",
|
|
32
|
+
},
|
|
33
|
+
"message_type": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"enum": ["text", "finding", "task", "query", "response"],
|
|
36
|
+
"description": "Structured message type (default: text)",
|
|
37
|
+
},
|
|
38
|
+
"thread_id": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Optional thread/conversation ID for grouping",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
"required": ["recipient", "message"],
|
|
44
|
+
},
|
|
45
|
+
),
|
|
46
|
+
Tool(
|
|
47
|
+
name="chat_history",
|
|
48
|
+
description=(
|
|
49
|
+
"Retrieve chat history from SKChat. Returns recent messages "
|
|
50
|
+
"with sender, content, type, thread, and timestamp. "
|
|
51
|
+
"Optionally filter by peer or thread."
|
|
52
|
+
),
|
|
53
|
+
inputSchema={
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"peer": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"description": "Filter by peer agent name or URI",
|
|
59
|
+
},
|
|
60
|
+
"thread_id": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Filter by thread/conversation ID",
|
|
63
|
+
},
|
|
64
|
+
"limit": {
|
|
65
|
+
"type": "integer",
|
|
66
|
+
"description": "Maximum messages to return (default: 20)",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
"required": [],
|
|
70
|
+
},
|
|
71
|
+
),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ── Helpers ──────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _get_skchat_identity() -> str:
|
|
79
|
+
"""Resolve the sovereign identity for SKChat operations."""
|
|
80
|
+
try:
|
|
81
|
+
from skchat.identity_bridge import get_sovereign_identity # type: ignore[import]
|
|
82
|
+
return get_sovereign_identity()
|
|
83
|
+
except ImportError:
|
|
84
|
+
from ..runtime import get_runtime
|
|
85
|
+
home = _home()
|
|
86
|
+
runtime = get_runtime(home)
|
|
87
|
+
return f"capauth:{runtime.manifest.name}@local"
|
|
88
|
+
except Exception:
|
|
89
|
+
return "capauth:agent@local"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _resolve_recipient(name: str) -> str:
|
|
93
|
+
"""Resolve a short agent name to a CapAuth URI if needed."""
|
|
94
|
+
if ":" in name:
|
|
95
|
+
return name
|
|
96
|
+
try:
|
|
97
|
+
from skchat.identity_bridge import resolve_peer_name # type: ignore[import]
|
|
98
|
+
return resolve_peer_name(name)
|
|
99
|
+
except Exception:
|
|
100
|
+
return f"capauth:{name}@local"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ── Handlers ─────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
async def _handle_chat_send(args: dict) -> list[TextContent]:
|
|
107
|
+
"""Send a chat message via SKChat AgentMessenger."""
|
|
108
|
+
try:
|
|
109
|
+
from skchat.agent_comm import AgentMessenger # type: ignore[import]
|
|
110
|
+
except ImportError:
|
|
111
|
+
return _error_response("skchat not installed. Run: pip install skchat")
|
|
112
|
+
|
|
113
|
+
recipient = args.get("recipient", "")
|
|
114
|
+
message = args.get("message", "")
|
|
115
|
+
if not recipient or not message:
|
|
116
|
+
return _error_response("recipient and message are required")
|
|
117
|
+
|
|
118
|
+
recipient_uri = _resolve_recipient(recipient)
|
|
119
|
+
identity = _get_skchat_identity()
|
|
120
|
+
|
|
121
|
+
messenger = AgentMessenger.from_identity(identity=identity)
|
|
122
|
+
result = messenger.send(
|
|
123
|
+
recipient=recipient_uri,
|
|
124
|
+
content=message,
|
|
125
|
+
message_type=args.get("message_type", "text"),
|
|
126
|
+
thread_id=args.get("thread_id"),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return _json_response({
|
|
130
|
+
"sent": True,
|
|
131
|
+
"message_id": result.get("message_id"),
|
|
132
|
+
"recipient": recipient_uri,
|
|
133
|
+
"delivered": result.get("delivered", False),
|
|
134
|
+
"transport": result.get("transport"),
|
|
135
|
+
"error": result.get("error"),
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
async def _handle_chat_history(args: dict) -> list[TextContent]:
|
|
140
|
+
"""Retrieve chat history from SKChat."""
|
|
141
|
+
try:
|
|
142
|
+
from skchat.history import ChatHistory # type: ignore[import]
|
|
143
|
+
except ImportError:
|
|
144
|
+
return _error_response("skchat not installed. Run: pip install skchat")
|
|
145
|
+
|
|
146
|
+
limit = args.get("limit", 20)
|
|
147
|
+
peer = args.get("peer")
|
|
148
|
+
thread_id = args.get("thread_id")
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
history = ChatHistory.from_config()
|
|
152
|
+
messages = history.recent(limit=limit, peer=peer, thread_id=thread_id)
|
|
153
|
+
return _json_response({
|
|
154
|
+
"count": len(messages),
|
|
155
|
+
"messages": [
|
|
156
|
+
{
|
|
157
|
+
"message_id": m.get("message_id") or m.get("id"),
|
|
158
|
+
"sender": m.get("sender"),
|
|
159
|
+
"recipient": m.get("recipient"),
|
|
160
|
+
"content": (m.get("content") or "")[:500],
|
|
161
|
+
"message_type": m.get("message_type", "text"),
|
|
162
|
+
"thread_id": m.get("thread_id"),
|
|
163
|
+
"timestamp": str(m.get("timestamp", "")),
|
|
164
|
+
}
|
|
165
|
+
for m in messages
|
|
166
|
+
],
|
|
167
|
+
})
|
|
168
|
+
except Exception as exc:
|
|
169
|
+
return _error_response(f"Could not retrieve chat history: {exc}")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
HANDLERS: dict = {
|
|
173
|
+
"chat_send": _handle_chat_send,
|
|
174
|
+
"chat_history": _handle_chat_history,
|
|
175
|
+
}
|