@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,44 @@
|
|
|
1
|
+
"""Service health check MCP tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _json_response
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="service_health",
|
|
12
|
+
description=(
|
|
13
|
+
"Check the health of all services in the sovereign stack. "
|
|
14
|
+
"Pings SKVector (Qdrant), SKGraph (FalkorDB), Syncthing, "
|
|
15
|
+
"skcapstone daemon, and skchat daemon. Returns status, "
|
|
16
|
+
"latency, version, and error details for each service."
|
|
17
|
+
),
|
|
18
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
19
|
+
),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def _handle_service_health(_args: dict) -> list[TextContent]:
|
|
24
|
+
"""Run health checks against all known services."""
|
|
25
|
+
from ..service_health import check_all_services
|
|
26
|
+
|
|
27
|
+
results = check_all_services()
|
|
28
|
+
up = sum(1 for r in results if r["status"] == "up")
|
|
29
|
+
down = sum(1 for r in results if r["status"] == "down")
|
|
30
|
+
|
|
31
|
+
return _json_response({
|
|
32
|
+
"summary": {
|
|
33
|
+
"total": len(results),
|
|
34
|
+
"up": up,
|
|
35
|
+
"down": down,
|
|
36
|
+
"unknown": len(results) - up - down,
|
|
37
|
+
},
|
|
38
|
+
"services": results,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
HANDLERS: dict = {
|
|
43
|
+
"service_health": _handle_service_health,
|
|
44
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Heartbeat beacon and peer discovery 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="heartbeat_pulse",
|
|
12
|
+
description=(
|
|
13
|
+
"Publish a heartbeat beacon for this agent. "
|
|
14
|
+
"Writes the agent's current state, capacity, and capabilities "
|
|
15
|
+
"to the shared heartbeats directory so peers can discover it."
|
|
16
|
+
),
|
|
17
|
+
inputSchema={
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"status": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Agent status: alive, busy, draining, offline (default: alive)",
|
|
23
|
+
},
|
|
24
|
+
"claimed_tasks": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {"type": "string"},
|
|
27
|
+
"description": "Currently claimed task IDs",
|
|
28
|
+
},
|
|
29
|
+
"loaded_model": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Currently loaded AI model name",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
"required": [],
|
|
35
|
+
},
|
|
36
|
+
),
|
|
37
|
+
Tool(
|
|
38
|
+
name="heartbeat_peers",
|
|
39
|
+
description=(
|
|
40
|
+
"Discover all peers in the agent mesh from heartbeat files. "
|
|
41
|
+
"Returns name, status, alive/stale, capabilities, and age "
|
|
42
|
+
"for each peer. Stale heartbeats (past TTL) are marked offline."
|
|
43
|
+
),
|
|
44
|
+
inputSchema={
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"include_self": {
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"description": "Include own heartbeat (default: false)",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
"required": [],
|
|
53
|
+
},
|
|
54
|
+
),
|
|
55
|
+
Tool(
|
|
56
|
+
name="heartbeat_health",
|
|
57
|
+
description=(
|
|
58
|
+
"Get overall mesh health summary: total peers, alive/offline "
|
|
59
|
+
"counts, aggregated capabilities across all live nodes."
|
|
60
|
+
),
|
|
61
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
62
|
+
),
|
|
63
|
+
Tool(
|
|
64
|
+
name="heartbeat_find_capable",
|
|
65
|
+
description=(
|
|
66
|
+
"Find alive peers with a specific capability. "
|
|
67
|
+
"Use this to locate agents that can perform a task."
|
|
68
|
+
),
|
|
69
|
+
inputSchema={
|
|
70
|
+
"type": "object",
|
|
71
|
+
"properties": {
|
|
72
|
+
"capability": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "The capability name to search for",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
"required": ["capability"],
|
|
78
|
+
},
|
|
79
|
+
),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def _handle_heartbeat_pulse(args: dict) -> list[TextContent]:
|
|
84
|
+
"""Publish a heartbeat beacon."""
|
|
85
|
+
from ..heartbeat import HeartbeatBeacon
|
|
86
|
+
|
|
87
|
+
home = _home()
|
|
88
|
+
agent_name = _get_agent_name(home)
|
|
89
|
+
shared = _shared_root()
|
|
90
|
+
beacon = HeartbeatBeacon(home, agent_name=agent_name,
|
|
91
|
+
heartbeats_dir=shared / "heartbeats")
|
|
92
|
+
beacon.initialize()
|
|
93
|
+
|
|
94
|
+
hb = beacon.pulse(
|
|
95
|
+
status=args.get("status", "alive"),
|
|
96
|
+
claimed_tasks=args.get("claimed_tasks"),
|
|
97
|
+
loaded_model=args.get("loaded_model", ""),
|
|
98
|
+
)
|
|
99
|
+
return _json_response({
|
|
100
|
+
"agent_name": hb.agent_name,
|
|
101
|
+
"status": hb.status,
|
|
102
|
+
"hostname": hb.hostname,
|
|
103
|
+
"platform": hb.platform,
|
|
104
|
+
"ttl_seconds": hb.ttl_seconds,
|
|
105
|
+
"uptime_hours": hb.uptime_hours,
|
|
106
|
+
"capabilities": [c.name for c in hb.capabilities],
|
|
107
|
+
"fingerprint": hb.fingerprint,
|
|
108
|
+
"capacity": {
|
|
109
|
+
"cpu_count": hb.capacity.cpu_count,
|
|
110
|
+
"memory_total_mb": hb.capacity.memory_total_mb,
|
|
111
|
+
"disk_free_gb": hb.capacity.disk_free_gb,
|
|
112
|
+
"gpu_available": hb.capacity.gpu_available,
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
async def _handle_heartbeat_peers(args: dict) -> list[TextContent]:
|
|
118
|
+
"""Discover peers in the mesh."""
|
|
119
|
+
from ..heartbeat import HeartbeatBeacon
|
|
120
|
+
|
|
121
|
+
home = _home()
|
|
122
|
+
agent_name = _get_agent_name(home)
|
|
123
|
+
shared = _shared_root()
|
|
124
|
+
beacon = HeartbeatBeacon(home, agent_name=agent_name,
|
|
125
|
+
heartbeats_dir=shared / "heartbeats")
|
|
126
|
+
beacon.initialize()
|
|
127
|
+
|
|
128
|
+
peers = beacon.discover_peers(include_self=args.get("include_self", False))
|
|
129
|
+
return _json_response([
|
|
130
|
+
{
|
|
131
|
+
"agent_name": p.agent_name,
|
|
132
|
+
"status": p.status,
|
|
133
|
+
"alive": p.alive,
|
|
134
|
+
"age_seconds": p.age_seconds,
|
|
135
|
+
"hostname": p.hostname,
|
|
136
|
+
"capabilities": p.capabilities,
|
|
137
|
+
"claimed_tasks": p.claimed_tasks,
|
|
138
|
+
}
|
|
139
|
+
for p in peers
|
|
140
|
+
])
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def _handle_heartbeat_health(_args: dict) -> list[TextContent]:
|
|
144
|
+
"""Get mesh health summary."""
|
|
145
|
+
from ..heartbeat import HeartbeatBeacon
|
|
146
|
+
|
|
147
|
+
home = _home()
|
|
148
|
+
agent_name = _get_agent_name(home)
|
|
149
|
+
shared = _shared_root()
|
|
150
|
+
beacon = HeartbeatBeacon(home, agent_name=agent_name,
|
|
151
|
+
heartbeats_dir=shared / "heartbeats")
|
|
152
|
+
beacon.initialize()
|
|
153
|
+
|
|
154
|
+
health = beacon.mesh_health()
|
|
155
|
+
return _json_response({
|
|
156
|
+
"total_peers": health.total_peers,
|
|
157
|
+
"alive_peers": health.alive_peers,
|
|
158
|
+
"offline_peers": health.offline_peers,
|
|
159
|
+
"busy_peers": health.busy_peers,
|
|
160
|
+
"total_capabilities": health.total_capabilities,
|
|
161
|
+
"peers": [
|
|
162
|
+
{"agent_name": p.agent_name, "status": p.status, "alive": p.alive}
|
|
163
|
+
for p in health.peers
|
|
164
|
+
],
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
async def _handle_heartbeat_find_capable(args: dict) -> list[TextContent]:
|
|
169
|
+
"""Find peers with a specific capability."""
|
|
170
|
+
from ..heartbeat import HeartbeatBeacon
|
|
171
|
+
|
|
172
|
+
home = _home()
|
|
173
|
+
agent_name = _get_agent_name(home)
|
|
174
|
+
shared = _shared_root()
|
|
175
|
+
beacon = HeartbeatBeacon(home, agent_name=agent_name,
|
|
176
|
+
heartbeats_dir=shared / "heartbeats")
|
|
177
|
+
beacon.initialize()
|
|
178
|
+
|
|
179
|
+
capability = args["capability"]
|
|
180
|
+
peers = beacon.find_capable(capability)
|
|
181
|
+
return _json_response({
|
|
182
|
+
"capability": capability,
|
|
183
|
+
"peers": [
|
|
184
|
+
{"agent_name": p.agent_name, "status": p.status, "capabilities": p.capabilities}
|
|
185
|
+
for p in peers
|
|
186
|
+
],
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
HANDLERS: dict = {
|
|
191
|
+
"heartbeat_pulse": _handle_heartbeat_pulse,
|
|
192
|
+
"heartbeat_peers": _handle_heartbeat_peers,
|
|
193
|
+
"heartbeat_health": _handle_heartbeat_health,
|
|
194
|
+
"heartbeat_find_capable": _handle_heartbeat_find_capable,
|
|
195
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""KMS (Key Management Service) 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="kms_status",
|
|
12
|
+
description=(
|
|
13
|
+
"Get KMS (Key Management Service) status: master key state, "
|
|
14
|
+
"total keys, active/revoked counts, service key inventory."
|
|
15
|
+
),
|
|
16
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
17
|
+
),
|
|
18
|
+
Tool(
|
|
19
|
+
name="kms_list_keys",
|
|
20
|
+
description=(
|
|
21
|
+
"List all keys in the KMS. Shows key ID, type, status, "
|
|
22
|
+
"label, creation date, and rotation count."
|
|
23
|
+
),
|
|
24
|
+
inputSchema={
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"key_type": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Filter by type: master, service, team, sub (omit for all)",
|
|
30
|
+
},
|
|
31
|
+
"include_revoked": {
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"description": "Include revoked keys (default: false)",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
"required": [],
|
|
37
|
+
},
|
|
38
|
+
),
|
|
39
|
+
Tool(
|
|
40
|
+
name="kms_rotate",
|
|
41
|
+
description=(
|
|
42
|
+
"Rotate a KMS key. Generates a new version of the key "
|
|
43
|
+
"and marks the old version as rotated. The old key material "
|
|
44
|
+
"remains available for decryption."
|
|
45
|
+
),
|
|
46
|
+
inputSchema={
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {
|
|
49
|
+
"key_id": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "The key ID to rotate",
|
|
52
|
+
},
|
|
53
|
+
"reason": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "Reason for rotation (default: 'scheduled')",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
"required": ["key_id"],
|
|
59
|
+
},
|
|
60
|
+
),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def _handle_kms_status(_args: dict) -> list[TextContent]:
|
|
65
|
+
"""Get KMS status."""
|
|
66
|
+
from ..kms import KeyStore
|
|
67
|
+
|
|
68
|
+
home = _home()
|
|
69
|
+
store = KeyStore(home)
|
|
70
|
+
store.initialize()
|
|
71
|
+
return _json_response(store.status())
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def _handle_kms_list_keys(args: dict) -> list[TextContent]:
|
|
75
|
+
"""List all KMS keys."""
|
|
76
|
+
from ..kms import KeyStore
|
|
77
|
+
|
|
78
|
+
home = _home()
|
|
79
|
+
store = KeyStore(home)
|
|
80
|
+
store.initialize()
|
|
81
|
+
|
|
82
|
+
key_type = args.get("key_type")
|
|
83
|
+
include_inactive = args.get("include_revoked", False)
|
|
84
|
+
keys = store.list_keys(key_type=key_type, include_inactive=include_inactive)
|
|
85
|
+
return _json_response([
|
|
86
|
+
{
|
|
87
|
+
"key_id": k.key_id,
|
|
88
|
+
"key_type": k.key_type,
|
|
89
|
+
"status": k.status,
|
|
90
|
+
"label": k.label,
|
|
91
|
+
"created_at": str(k.created_at),
|
|
92
|
+
"version": k.version,
|
|
93
|
+
"algorithm": k.algorithm,
|
|
94
|
+
}
|
|
95
|
+
for k in keys
|
|
96
|
+
])
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def _handle_kms_rotate(args: dict) -> list[TextContent]:
|
|
100
|
+
"""Rotate a KMS key."""
|
|
101
|
+
from ..kms import KeyStore
|
|
102
|
+
|
|
103
|
+
home = _home()
|
|
104
|
+
store = KeyStore(home)
|
|
105
|
+
store.initialize()
|
|
106
|
+
|
|
107
|
+
new_key = store.rotate_key(
|
|
108
|
+
key_id=args["key_id"],
|
|
109
|
+
reason=args.get("reason", "scheduled"),
|
|
110
|
+
)
|
|
111
|
+
return _json_response({
|
|
112
|
+
"key_id": new_key.key_id,
|
|
113
|
+
"version": new_key.version,
|
|
114
|
+
"status": new_key.status,
|
|
115
|
+
"message": f"Key rotated to version {new_key.version}",
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
HANDLERS: dict = {
|
|
120
|
+
"kms_status": _handle_kms_status,
|
|
121
|
+
"kms_list_keys": _handle_kms_list_keys,
|
|
122
|
+
"kms_rotate": _handle_kms_rotate,
|
|
123
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""Memory store, search, recall, and curation tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _error_response, _home, _json_response
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="memory_store",
|
|
12
|
+
description=(
|
|
13
|
+
"Store a new memory in the agent's persistent memory. "
|
|
14
|
+
"Memories start in short-term and promote based on "
|
|
15
|
+
"access patterns and importance."
|
|
16
|
+
),
|
|
17
|
+
inputSchema={
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"content": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "The memory content (free-text)",
|
|
23
|
+
},
|
|
24
|
+
"tags": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {"type": "string"},
|
|
27
|
+
"description": "Tags for categorization",
|
|
28
|
+
},
|
|
29
|
+
"importance": {
|
|
30
|
+
"type": "number",
|
|
31
|
+
"description": "Importance score 0.0-1.0 (>= 0.7 auto-promotes to mid-term)",
|
|
32
|
+
},
|
|
33
|
+
"source": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Where this memory came from (default: mcp)",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
"required": ["content"],
|
|
39
|
+
},
|
|
40
|
+
),
|
|
41
|
+
Tool(
|
|
42
|
+
name="memory_search",
|
|
43
|
+
description=(
|
|
44
|
+
"Search the agent's memories by query string. "
|
|
45
|
+
"Full-text search across all layers, ranked by relevance."
|
|
46
|
+
),
|
|
47
|
+
inputSchema={
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"query": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Search query",
|
|
53
|
+
},
|
|
54
|
+
"limit": {
|
|
55
|
+
"type": "integer",
|
|
56
|
+
"description": "Max results (default: 10)",
|
|
57
|
+
},
|
|
58
|
+
"tags": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": {"type": "string"},
|
|
61
|
+
"description": "Filter by tags (all must match)",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
"required": ["query"],
|
|
65
|
+
},
|
|
66
|
+
),
|
|
67
|
+
Tool(
|
|
68
|
+
name="memory_recall",
|
|
69
|
+
description=(
|
|
70
|
+
"Recall a specific memory by its ID. Returns full content "
|
|
71
|
+
"and increments the access counter (frequent access promotes memories)."
|
|
72
|
+
),
|
|
73
|
+
inputSchema={
|
|
74
|
+
"type": "object",
|
|
75
|
+
"properties": {
|
|
76
|
+
"memory_id": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "The memory's unique ID",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
"required": ["memory_id"],
|
|
82
|
+
},
|
|
83
|
+
),
|
|
84
|
+
Tool(
|
|
85
|
+
name="memory_curate",
|
|
86
|
+
description=(
|
|
87
|
+
"Run a curation pass over the agent's memories. "
|
|
88
|
+
"Auto-tags untagged memories, promotes qualifying "
|
|
89
|
+
"memories to higher tiers, and removes duplicates. "
|
|
90
|
+
"Use dry_run=true to preview without changes."
|
|
91
|
+
),
|
|
92
|
+
inputSchema={
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {
|
|
95
|
+
"dry_run": {
|
|
96
|
+
"type": "boolean",
|
|
97
|
+
"description": "Preview changes without applying (default: false)",
|
|
98
|
+
},
|
|
99
|
+
"stats_only": {
|
|
100
|
+
"type": "boolean",
|
|
101
|
+
"description": "Return statistics instead of curating (default: false)",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
"required": [],
|
|
105
|
+
},
|
|
106
|
+
),
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def _handle_memory_store(args: dict) -> list[TextContent]:
|
|
111
|
+
"""Store a new memory."""
|
|
112
|
+
from ..memory_engine import store
|
|
113
|
+
|
|
114
|
+
content = args.get("content", "")
|
|
115
|
+
if not content:
|
|
116
|
+
return _error_response("content is required")
|
|
117
|
+
|
|
118
|
+
entry = store(
|
|
119
|
+
home=_home(),
|
|
120
|
+
content=content,
|
|
121
|
+
tags=args.get("tags", []),
|
|
122
|
+
source=args.get("source", "mcp"),
|
|
123
|
+
importance=args.get("importance", 0.5),
|
|
124
|
+
)
|
|
125
|
+
try:
|
|
126
|
+
from .. import activity
|
|
127
|
+
activity.push("memory.stored", {
|
|
128
|
+
"memory_id": entry.memory_id,
|
|
129
|
+
"layer": entry.layer.value,
|
|
130
|
+
"importance": entry.importance,
|
|
131
|
+
"tags": entry.tags,
|
|
132
|
+
})
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
return _json_response({
|
|
136
|
+
"memory_id": entry.memory_id,
|
|
137
|
+
"layer": entry.layer.value,
|
|
138
|
+
"importance": entry.importance,
|
|
139
|
+
"tags": entry.tags,
|
|
140
|
+
"stored": True,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
async def _handle_memory_search(args: dict) -> list[TextContent]:
|
|
145
|
+
"""Search memories by query."""
|
|
146
|
+
from ..memory_engine import search
|
|
147
|
+
|
|
148
|
+
query = args.get("query", "")
|
|
149
|
+
if not query:
|
|
150
|
+
return _error_response("query is required")
|
|
151
|
+
|
|
152
|
+
results = search(
|
|
153
|
+
home=_home(),
|
|
154
|
+
query=query,
|
|
155
|
+
tags=args.get("tags"),
|
|
156
|
+
limit=args.get("limit", 10),
|
|
157
|
+
)
|
|
158
|
+
return _json_response([
|
|
159
|
+
{
|
|
160
|
+
"memory_id": e.memory_id,
|
|
161
|
+
"layer": e.layer.value,
|
|
162
|
+
"content": e.content[:300],
|
|
163
|
+
"tags": e.tags,
|
|
164
|
+
"importance": e.importance,
|
|
165
|
+
"access_count": e.access_count,
|
|
166
|
+
}
|
|
167
|
+
for e in results
|
|
168
|
+
])
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
async def _handle_memory_recall(args: dict) -> list[TextContent]:
|
|
172
|
+
"""Recall a specific memory by ID."""
|
|
173
|
+
from ..memory_engine import recall
|
|
174
|
+
|
|
175
|
+
memory_id = args.get("memory_id", "")
|
|
176
|
+
if not memory_id:
|
|
177
|
+
return _error_response("memory_id is required")
|
|
178
|
+
|
|
179
|
+
entry = recall(home=_home(), memory_id=memory_id)
|
|
180
|
+
if entry is None:
|
|
181
|
+
return _error_response(f"Memory not found: {memory_id}")
|
|
182
|
+
|
|
183
|
+
return _json_response({
|
|
184
|
+
"memory_id": entry.memory_id,
|
|
185
|
+
"content": entry.content,
|
|
186
|
+
"layer": entry.layer.value,
|
|
187
|
+
"tags": entry.tags,
|
|
188
|
+
"importance": entry.importance,
|
|
189
|
+
"access_count": entry.access_count,
|
|
190
|
+
"source": entry.source,
|
|
191
|
+
"created_at": entry.created_at.isoformat() if entry.created_at else None,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
async def _handle_memory_curate(args: dict) -> list[TextContent]:
|
|
196
|
+
"""Run a memory curation pass or return stats."""
|
|
197
|
+
from ..memory_curator import MemoryCurator
|
|
198
|
+
|
|
199
|
+
home = _home()
|
|
200
|
+
curator = MemoryCurator(home)
|
|
201
|
+
|
|
202
|
+
if args.get("stats_only"):
|
|
203
|
+
return _json_response(curator.get_stats())
|
|
204
|
+
|
|
205
|
+
dry_run = args.get("dry_run", False)
|
|
206
|
+
result = curator.curate(dry_run=dry_run)
|
|
207
|
+
return _json_response({
|
|
208
|
+
"dry_run": dry_run,
|
|
209
|
+
"scanned": result.total_scanned,
|
|
210
|
+
"tagged": len(result.tagged),
|
|
211
|
+
"promoted": len(result.promoted),
|
|
212
|
+
"deduped": len(result.deduped),
|
|
213
|
+
"by_layer": result.by_layer,
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
HANDLERS: dict = {
|
|
218
|
+
"memory_store": _handle_memory_store,
|
|
219
|
+
"memory_search": _handle_memory_search,
|
|
220
|
+
"memory_recall": _handle_memory_recall,
|
|
221
|
+
"memory_curate": _handle_memory_curate,
|
|
222
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Model router tool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _json_response
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="model_route",
|
|
12
|
+
description=(
|
|
13
|
+
"Route a task to the optimal model tier and concrete model name. "
|
|
14
|
+
"Accepts a task description, optional tags, privacy/localhost flags, "
|
|
15
|
+
"and token estimate. Returns the selected tier (fast/code/reason/"
|
|
16
|
+
"nuance/local), model name, and reasoning. Use this to automatically "
|
|
17
|
+
"select the best model for any task based on complexity, type, and "
|
|
18
|
+
"constraints."
|
|
19
|
+
),
|
|
20
|
+
inputSchema={
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"description": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "What the task is about",
|
|
26
|
+
},
|
|
27
|
+
"tags": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": {"type": "string"},
|
|
30
|
+
"description": (
|
|
31
|
+
"Classification tags (e.g. ['code', 'refactor']). "
|
|
32
|
+
"Used for rule-based tier matching."
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
"requires_localhost": {
|
|
36
|
+
"type": "boolean",
|
|
37
|
+
"description": "Force LOCAL tier on originating node (default: false)",
|
|
38
|
+
},
|
|
39
|
+
"privacy_sensitive": {
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"description": "Force LOCAL tier \u2014 data never leaves node (default: false)",
|
|
42
|
+
},
|
|
43
|
+
"estimated_tokens": {
|
|
44
|
+
"type": "integer",
|
|
45
|
+
"description": (
|
|
46
|
+
"Rough token budget hint. Tasks > 16000 tokens "
|
|
47
|
+
"default to REASON tier when no tag rule matches."
|
|
48
|
+
),
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"required": ["description"],
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def _handle_model_route(args: dict) -> list[TextContent]:
|
|
58
|
+
"""Route a task to the optimal model tier and name."""
|
|
59
|
+
from ..model_router import ModelRouter, TaskSignal
|
|
60
|
+
|
|
61
|
+
signal = TaskSignal(
|
|
62
|
+
description=args.get("description", ""),
|
|
63
|
+
tags=args.get("tags", []),
|
|
64
|
+
requires_localhost=args.get("requires_localhost", False),
|
|
65
|
+
privacy_sensitive=args.get("privacy_sensitive", False),
|
|
66
|
+
estimated_tokens=args.get("estimated_tokens", 0),
|
|
67
|
+
)
|
|
68
|
+
router = ModelRouter()
|
|
69
|
+
decision = router.route(signal)
|
|
70
|
+
return _json_response(decision.model_dump())
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
HANDLERS: dict = {
|
|
74
|
+
"model_route": _handle_model_route,
|
|
75
|
+
}
|