@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,674 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config file validator for SKCapstone.
|
|
3
|
+
|
|
4
|
+
Validates consciousness.yaml, model_profiles.yaml, identity.json,
|
|
5
|
+
and soul blueprints. Reports errors with file paths and line numbers.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from skcapstone.config_validator import validate_all
|
|
9
|
+
report = validate_all(home_path)
|
|
10
|
+
if not report.is_valid:
|
|
11
|
+
for r in report.results:
|
|
12
|
+
for issue in r.errors:
|
|
13
|
+
print(issue)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import re
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any, Optional
|
|
23
|
+
|
|
24
|
+
import yaml
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Result models
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ValidationIssue:
|
|
34
|
+
"""A single validation problem in a config file."""
|
|
35
|
+
|
|
36
|
+
severity: str # "error" | "warning"
|
|
37
|
+
message: str
|
|
38
|
+
field: Optional[str] = None
|
|
39
|
+
line: Optional[int] = None
|
|
40
|
+
|
|
41
|
+
def __str__(self) -> str:
|
|
42
|
+
loc = f"line {self.line}" if self.line else ""
|
|
43
|
+
field_str = f" [{self.field}]" if self.field else ""
|
|
44
|
+
suffix = f" ({loc})" if loc else ""
|
|
45
|
+
return f"{self.severity.upper()}{field_str}{suffix}: {self.message}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class FileValidationResult:
|
|
50
|
+
"""Validation outcome for a single config file."""
|
|
51
|
+
|
|
52
|
+
config_name: str
|
|
53
|
+
file_path: Path
|
|
54
|
+
found: bool = True
|
|
55
|
+
issues: list[ValidationIssue] = field(default_factory=list)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def errors(self) -> list[ValidationIssue]:
|
|
59
|
+
return [i for i in self.issues if i.severity == "error"]
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def warnings(self) -> list[ValidationIssue]:
|
|
63
|
+
return [i for i in self.issues if i.severity == "warning"]
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def is_valid(self) -> bool:
|
|
67
|
+
"""True when there are no errors.
|
|
68
|
+
|
|
69
|
+
A missing file is considered valid — it simply falls back to
|
|
70
|
+
built-in defaults and produces a warning, not an error.
|
|
71
|
+
"""
|
|
72
|
+
return len(self.errors) == 0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class ConfigValidationReport:
|
|
77
|
+
"""Aggregated validation report across all config files."""
|
|
78
|
+
|
|
79
|
+
results: list[FileValidationResult] = field(default_factory=list)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def is_valid(self) -> bool:
|
|
83
|
+
return all(r.is_valid for r in self.results)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def total_errors(self) -> int:
|
|
87
|
+
return sum(len(r.errors) for r in self.results)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def total_warnings(self) -> int:
|
|
91
|
+
return sum(len(r.warnings) for r in self.results)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
# YAML AST helpers for line-number extraction
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _yaml_key_line(text: str, key: str) -> Optional[int]:
|
|
100
|
+
"""Return the 1-based line number of a top-level YAML key.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
text: Raw YAML text.
|
|
104
|
+
key: Key name to locate.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Line number (1-based) or None if not found / parse fails.
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
node = yaml.compose(text)
|
|
111
|
+
if not isinstance(node, yaml.MappingNode):
|
|
112
|
+
return None
|
|
113
|
+
for key_node, _ in node.value:
|
|
114
|
+
if isinstance(key_node, yaml.ScalarNode) and key_node.value == key:
|
|
115
|
+
return key_node.start_mark.line + 1
|
|
116
|
+
except Exception:
|
|
117
|
+
pass
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _yaml_seq_item_line(text: str, seq_key: str, idx: int, subkey: str) -> Optional[int]:
|
|
122
|
+
"""Return the line of *subkey* inside a sequence-item mapping.
|
|
123
|
+
|
|
124
|
+
Navigates ``{seq_key: [{subkey: …}, …]}`` and returns the line of
|
|
125
|
+
*subkey* in the *idx*-th item. Falls back to the item's opening line
|
|
126
|
+
if *subkey* is absent.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
text: Raw YAML text.
|
|
130
|
+
seq_key: Top-level sequence key (e.g. ``"profiles"``).
|
|
131
|
+
idx: Zero-based index into the sequence.
|
|
132
|
+
subkey: Key to locate inside the item mapping.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Line number (1-based) or None.
|
|
136
|
+
"""
|
|
137
|
+
try:
|
|
138
|
+
root = yaml.compose(text)
|
|
139
|
+
if not isinstance(root, yaml.MappingNode):
|
|
140
|
+
return None
|
|
141
|
+
for k, v in root.value:
|
|
142
|
+
if k.value == seq_key and isinstance(v, yaml.SequenceNode):
|
|
143
|
+
if idx < len(v.value):
|
|
144
|
+
item = v.value[idx]
|
|
145
|
+
if isinstance(item, yaml.MappingNode):
|
|
146
|
+
for kk, _ in item.value:
|
|
147
|
+
if kk.value == subkey:
|
|
148
|
+
return kk.start_mark.line + 1
|
|
149
|
+
return item.start_mark.line + 1
|
|
150
|
+
except Exception:
|
|
151
|
+
pass
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
# Allowed value sets
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
_VALID_FALLBACK_BACKENDS = frozenset([
|
|
160
|
+
"ollama", "grok", "kimi", "nvidia", "anthropic",
|
|
161
|
+
"openai", "passthrough", "groq", "mistral", "perplexity",
|
|
162
|
+
])
|
|
163
|
+
_VALID_SYSTEM_PROMPT_MODES = frozenset(["standard", "separate_param", "omit"])
|
|
164
|
+
_VALID_STRUCTURE_FORMATS = frozenset(["xml", "markdown", "plain"])
|
|
165
|
+
_VALID_THINKING_MODES = frozenset(["none", "budget", "toggle", "auto"])
|
|
166
|
+
_VALID_TOOL_FORMATS = frozenset(["openai", "anthropic", "mistral"])
|
|
167
|
+
|
|
168
|
+
_CONSCIOUSNESS_KNOWN_KEYS = frozenset([
|
|
169
|
+
"enabled", "use_inotify", "inotify_debounce_ms", "response_timeout",
|
|
170
|
+
"max_context_tokens", "max_history_messages", "auto_memory", "auto_ack",
|
|
171
|
+
"privacy_default", "max_concurrent_requests", "fallback_chain",
|
|
172
|
+
"desktop_notifications",
|
|
173
|
+
])
|
|
174
|
+
|
|
175
|
+
_IDENTITY_FINGERPRINT_RE = re.compile(r"^[0-9A-Fa-f]{40}$")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# consciousness.yaml validator
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def validate_consciousness_yaml(path: Path) -> FileValidationResult:
|
|
184
|
+
"""Validate consciousness.yaml against the ConsciousnessConfig schema.
|
|
185
|
+
|
|
186
|
+
Checks types, value ranges, fallback chain entries, and unknown keys.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
path: Path to the consciousness.yaml file.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
FileValidationResult with any errors/warnings found.
|
|
193
|
+
"""
|
|
194
|
+
result = FileValidationResult(config_name="consciousness.yaml", file_path=path)
|
|
195
|
+
|
|
196
|
+
if not path.exists():
|
|
197
|
+
result.found = False
|
|
198
|
+
result.issues.append(ValidationIssue(
|
|
199
|
+
severity="warning",
|
|
200
|
+
message="File not found — defaults will be used",
|
|
201
|
+
))
|
|
202
|
+
return result
|
|
203
|
+
|
|
204
|
+
text = path.read_text(encoding="utf-8")
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
raw: Any = yaml.safe_load(text)
|
|
208
|
+
except yaml.YAMLError as exc:
|
|
209
|
+
line = None
|
|
210
|
+
if hasattr(exc, "problem_mark") and exc.problem_mark:
|
|
211
|
+
line = exc.problem_mark.line + 1
|
|
212
|
+
result.issues.append(ValidationIssue(
|
|
213
|
+
severity="error", message=f"YAML parse error: {exc}", line=line,
|
|
214
|
+
))
|
|
215
|
+
return result
|
|
216
|
+
|
|
217
|
+
if not raw:
|
|
218
|
+
result.issues.append(ValidationIssue(
|
|
219
|
+
severity="warning", message="Empty file — defaults will be used",
|
|
220
|
+
))
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
if not isinstance(raw, dict):
|
|
224
|
+
result.issues.append(ValidationIssue(
|
|
225
|
+
severity="error",
|
|
226
|
+
message="Top-level value must be a YAML mapping",
|
|
227
|
+
line=1,
|
|
228
|
+
))
|
|
229
|
+
return result
|
|
230
|
+
|
|
231
|
+
# ── Bool fields ──────────────────────────────────────────────────────
|
|
232
|
+
for bf in ("enabled", "use_inotify", "auto_memory", "auto_ack",
|
|
233
|
+
"privacy_default", "desktop_notifications"):
|
|
234
|
+
if bf in raw and not isinstance(raw[bf], bool):
|
|
235
|
+
result.issues.append(ValidationIssue(
|
|
236
|
+
severity="error", field=bf,
|
|
237
|
+
line=_yaml_key_line(text, bf),
|
|
238
|
+
message=f"Expected bool, got {type(raw[bf]).__name__}",
|
|
239
|
+
))
|
|
240
|
+
|
|
241
|
+
# ── Positive-int fields ──────────────────────────────────────────────
|
|
242
|
+
_pos_int: dict[str, int] = {
|
|
243
|
+
"inotify_debounce_ms": 0,
|
|
244
|
+
"response_timeout": 1,
|
|
245
|
+
"max_context_tokens": 1,
|
|
246
|
+
"max_history_messages": 0,
|
|
247
|
+
"max_concurrent_requests": 1,
|
|
248
|
+
}
|
|
249
|
+
for fi, min_val in _pos_int.items():
|
|
250
|
+
if fi not in raw:
|
|
251
|
+
continue
|
|
252
|
+
v = raw[fi]
|
|
253
|
+
line = _yaml_key_line(text, fi)
|
|
254
|
+
if not isinstance(v, int):
|
|
255
|
+
result.issues.append(ValidationIssue(
|
|
256
|
+
severity="error", field=fi, line=line,
|
|
257
|
+
message=f"Expected int, got {type(v).__name__}",
|
|
258
|
+
))
|
|
259
|
+
elif v <= min_val:
|
|
260
|
+
result.issues.append(ValidationIssue(
|
|
261
|
+
severity="error", field=fi, line=line,
|
|
262
|
+
message=f"Must be > {min_val}, got {v}",
|
|
263
|
+
))
|
|
264
|
+
|
|
265
|
+
# ── fallback_chain ───────────────────────────────────────────────────
|
|
266
|
+
if "fallback_chain" in raw:
|
|
267
|
+
chain = raw["fallback_chain"]
|
|
268
|
+
line = _yaml_key_line(text, "fallback_chain")
|
|
269
|
+
if not isinstance(chain, list):
|
|
270
|
+
result.issues.append(ValidationIssue(
|
|
271
|
+
severity="error", field="fallback_chain", line=line,
|
|
272
|
+
message=f"Expected list, got {type(chain).__name__}",
|
|
273
|
+
))
|
|
274
|
+
else:
|
|
275
|
+
for i, item in enumerate(chain):
|
|
276
|
+
if not isinstance(item, str):
|
|
277
|
+
result.issues.append(ValidationIssue(
|
|
278
|
+
severity="error",
|
|
279
|
+
field=f"fallback_chain[{i}]",
|
|
280
|
+
message=f"Expected string, got {type(item).__name__}",
|
|
281
|
+
))
|
|
282
|
+
elif item not in _VALID_FALLBACK_BACKENDS:
|
|
283
|
+
result.issues.append(ValidationIssue(
|
|
284
|
+
severity="warning",
|
|
285
|
+
field=f"fallback_chain[{i}]",
|
|
286
|
+
message=f"Unknown backend '{item}' (may be a custom provider)",
|
|
287
|
+
))
|
|
288
|
+
|
|
289
|
+
# ── Unknown keys → warnings ──────────────────────────────────────────
|
|
290
|
+
for key in raw:
|
|
291
|
+
if key not in _CONSCIOUSNESS_KNOWN_KEYS:
|
|
292
|
+
result.issues.append(ValidationIssue(
|
|
293
|
+
severity="warning", field=key,
|
|
294
|
+
line=_yaml_key_line(text, key),
|
|
295
|
+
message=f"Unknown config key '{key}'",
|
|
296
|
+
))
|
|
297
|
+
|
|
298
|
+
return result
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# ---------------------------------------------------------------------------
|
|
302
|
+
# model_profiles.yaml validator
|
|
303
|
+
# ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def validate_model_profiles_yaml(path: Path) -> FileValidationResult:
|
|
307
|
+
"""Validate model_profiles.yaml profile list and field types.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
path: Path to model_profiles.yaml.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
FileValidationResult with any errors/warnings found.
|
|
314
|
+
"""
|
|
315
|
+
result = FileValidationResult(config_name="model_profiles.yaml", file_path=path)
|
|
316
|
+
|
|
317
|
+
if not path.exists():
|
|
318
|
+
result.found = False
|
|
319
|
+
result.issues.append(ValidationIssue(
|
|
320
|
+
severity="warning",
|
|
321
|
+
message="File not found — bundled defaults will be used",
|
|
322
|
+
))
|
|
323
|
+
return result
|
|
324
|
+
|
|
325
|
+
text = path.read_text(encoding="utf-8")
|
|
326
|
+
|
|
327
|
+
try:
|
|
328
|
+
raw: Any = yaml.safe_load(text)
|
|
329
|
+
except yaml.YAMLError as exc:
|
|
330
|
+
line = None
|
|
331
|
+
if hasattr(exc, "problem_mark") and exc.problem_mark:
|
|
332
|
+
line = exc.problem_mark.line + 1
|
|
333
|
+
result.issues.append(ValidationIssue(
|
|
334
|
+
severity="error", message=f"YAML parse error: {exc}", line=line,
|
|
335
|
+
))
|
|
336
|
+
return result
|
|
337
|
+
|
|
338
|
+
if not isinstance(raw, dict):
|
|
339
|
+
result.issues.append(ValidationIssue(
|
|
340
|
+
severity="error",
|
|
341
|
+
message="Top-level value must be a YAML mapping",
|
|
342
|
+
line=1,
|
|
343
|
+
))
|
|
344
|
+
return result
|
|
345
|
+
|
|
346
|
+
if "profiles" not in raw:
|
|
347
|
+
result.issues.append(ValidationIssue(
|
|
348
|
+
severity="error", field="profiles",
|
|
349
|
+
message="Required top-level key 'profiles' is missing",
|
|
350
|
+
line=1,
|
|
351
|
+
))
|
|
352
|
+
return result
|
|
353
|
+
|
|
354
|
+
profiles = raw["profiles"]
|
|
355
|
+
profiles_line = _yaml_key_line(text, "profiles")
|
|
356
|
+
|
|
357
|
+
if not isinstance(profiles, list):
|
|
358
|
+
result.issues.append(ValidationIssue(
|
|
359
|
+
severity="error", field="profiles", line=profiles_line,
|
|
360
|
+
message=f"'profiles' must be a list, got {type(profiles).__name__}",
|
|
361
|
+
))
|
|
362
|
+
return result
|
|
363
|
+
|
|
364
|
+
if len(profiles) == 0:
|
|
365
|
+
result.issues.append(ValidationIssue(
|
|
366
|
+
severity="warning", field="profiles", line=profiles_line,
|
|
367
|
+
message="'profiles' list is empty",
|
|
368
|
+
))
|
|
369
|
+
return result
|
|
370
|
+
|
|
371
|
+
_enum_checks: list[tuple[str, frozenset[str]]] = [
|
|
372
|
+
("system_prompt_mode", _VALID_SYSTEM_PROMPT_MODES),
|
|
373
|
+
("structure_format", _VALID_STRUCTURE_FORMATS),
|
|
374
|
+
("thinking_mode", _VALID_THINKING_MODES),
|
|
375
|
+
("tool_format", _VALID_TOOL_FORMATS),
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
for i, profile in enumerate(profiles):
|
|
379
|
+
prefix = f"profiles[{i}]"
|
|
380
|
+
item_line = _yaml_seq_item_line(text, "profiles", i, "model_pattern")
|
|
381
|
+
|
|
382
|
+
if not isinstance(profile, dict):
|
|
383
|
+
result.issues.append(ValidationIssue(
|
|
384
|
+
severity="error", field=prefix, line=item_line,
|
|
385
|
+
message=f"Each profile must be a mapping, got {type(profile).__name__}",
|
|
386
|
+
))
|
|
387
|
+
continue
|
|
388
|
+
|
|
389
|
+
# Required: model_pattern + family
|
|
390
|
+
for req in ("model_pattern", "family"):
|
|
391
|
+
if req not in profile:
|
|
392
|
+
result.issues.append(ValidationIssue(
|
|
393
|
+
severity="error",
|
|
394
|
+
field=f"{prefix}.{req}",
|
|
395
|
+
line=item_line,
|
|
396
|
+
message=f"Required field '{req}' is missing",
|
|
397
|
+
))
|
|
398
|
+
elif not isinstance(profile[req], str):
|
|
399
|
+
result.issues.append(ValidationIssue(
|
|
400
|
+
severity="error",
|
|
401
|
+
field=f"{prefix}.{req}",
|
|
402
|
+
line=_yaml_seq_item_line(text, "profiles", i, req),
|
|
403
|
+
message=f"Expected str, got {type(profile[req]).__name__}",
|
|
404
|
+
))
|
|
405
|
+
|
|
406
|
+
# model_pattern must be a valid regex
|
|
407
|
+
pat = profile.get("model_pattern")
|
|
408
|
+
if isinstance(pat, str):
|
|
409
|
+
try:
|
|
410
|
+
re.compile(pat)
|
|
411
|
+
except re.error as exc:
|
|
412
|
+
result.issues.append(ValidationIssue(
|
|
413
|
+
severity="error",
|
|
414
|
+
field=f"{prefix}.model_pattern",
|
|
415
|
+
line=_yaml_seq_item_line(text, "profiles", i, "model_pattern"),
|
|
416
|
+
message=f"Invalid regex: {exc}",
|
|
417
|
+
))
|
|
418
|
+
|
|
419
|
+
# Enum fields
|
|
420
|
+
for fname, valid_set in _enum_checks:
|
|
421
|
+
if fname in profile:
|
|
422
|
+
val = profile[fname]
|
|
423
|
+
if isinstance(val, str) and val not in valid_set:
|
|
424
|
+
result.issues.append(ValidationIssue(
|
|
425
|
+
severity="error",
|
|
426
|
+
field=f"{prefix}.{fname}",
|
|
427
|
+
line=_yaml_seq_item_line(text, "profiles", i, fname),
|
|
428
|
+
message=(
|
|
429
|
+
f"Invalid value '{val}', "
|
|
430
|
+
f"expected one of: {sorted(valid_set)}"
|
|
431
|
+
),
|
|
432
|
+
))
|
|
433
|
+
|
|
434
|
+
# Bool fields
|
|
435
|
+
for bf in ("thinking_enabled", "no_few_shot",
|
|
436
|
+
"no_cot_instructions", "supports_tool_calling"):
|
|
437
|
+
if bf in profile and not isinstance(profile[bf], bool):
|
|
438
|
+
result.issues.append(ValidationIssue(
|
|
439
|
+
severity="error",
|
|
440
|
+
field=f"{prefix}.{bf}",
|
|
441
|
+
line=_yaml_seq_item_line(text, "profiles", i, bf),
|
|
442
|
+
message=f"Expected bool, got {type(profile[bf]).__name__}",
|
|
443
|
+
))
|
|
444
|
+
|
|
445
|
+
return result
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
# ---------------------------------------------------------------------------
|
|
449
|
+
# identity.json validator
|
|
450
|
+
# ---------------------------------------------------------------------------
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def validate_identity_json(path: Path) -> FileValidationResult:
|
|
454
|
+
"""Validate identity.json required fields and formats.
|
|
455
|
+
|
|
456
|
+
Checks that ``name`` and ``fingerprint`` are present and well-formed.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
path: Path to identity.json.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
FileValidationResult with any errors/warnings found.
|
|
463
|
+
"""
|
|
464
|
+
result = FileValidationResult(config_name="identity.json", file_path=path)
|
|
465
|
+
|
|
466
|
+
if not path.exists():
|
|
467
|
+
result.found = False
|
|
468
|
+
result.issues.append(ValidationIssue(
|
|
469
|
+
severity="warning",
|
|
470
|
+
message="File not found — run 'skcapstone init' to create identity",
|
|
471
|
+
))
|
|
472
|
+
return result
|
|
473
|
+
|
|
474
|
+
text = path.read_text(encoding="utf-8")
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
raw: Any = json.loads(text)
|
|
478
|
+
except json.JSONDecodeError as exc:
|
|
479
|
+
result.issues.append(ValidationIssue(
|
|
480
|
+
severity="error",
|
|
481
|
+
message=f"JSON parse error: {exc.msg}",
|
|
482
|
+
line=exc.lineno,
|
|
483
|
+
))
|
|
484
|
+
return result
|
|
485
|
+
|
|
486
|
+
if not isinstance(raw, dict):
|
|
487
|
+
result.issues.append(ValidationIssue(
|
|
488
|
+
severity="error",
|
|
489
|
+
message="Top-level value must be a JSON object",
|
|
490
|
+
line=1,
|
|
491
|
+
))
|
|
492
|
+
return result
|
|
493
|
+
|
|
494
|
+
# Required: name
|
|
495
|
+
if "name" not in raw:
|
|
496
|
+
result.issues.append(ValidationIssue(
|
|
497
|
+
severity="error", field="name",
|
|
498
|
+
message="Required field 'name' is missing",
|
|
499
|
+
))
|
|
500
|
+
elif not isinstance(raw["name"], str) or not raw["name"].strip():
|
|
501
|
+
result.issues.append(ValidationIssue(
|
|
502
|
+
severity="error", field="name",
|
|
503
|
+
message="'name' must be a non-empty string",
|
|
504
|
+
))
|
|
505
|
+
|
|
506
|
+
# Required: fingerprint
|
|
507
|
+
if "fingerprint" not in raw:
|
|
508
|
+
result.issues.append(ValidationIssue(
|
|
509
|
+
severity="error", field="fingerprint",
|
|
510
|
+
message="Required field 'fingerprint' is missing",
|
|
511
|
+
))
|
|
512
|
+
elif not isinstance(raw["fingerprint"], str):
|
|
513
|
+
result.issues.append(ValidationIssue(
|
|
514
|
+
severity="error", field="fingerprint",
|
|
515
|
+
message=f"Expected str, got {type(raw['fingerprint']).__name__}",
|
|
516
|
+
))
|
|
517
|
+
elif not _IDENTITY_FINGERPRINT_RE.match(raw["fingerprint"]):
|
|
518
|
+
result.issues.append(ValidationIssue(
|
|
519
|
+
severity="warning", field="fingerprint",
|
|
520
|
+
message=(
|
|
521
|
+
f"Fingerprint '{raw['fingerprint']}' does not look like a "
|
|
522
|
+
"PGP fingerprint (expected 40 hex characters)"
|
|
523
|
+
),
|
|
524
|
+
))
|
|
525
|
+
|
|
526
|
+
# Optional: email must be str if present
|
|
527
|
+
if "email" in raw and raw["email"] is not None:
|
|
528
|
+
if not isinstance(raw["email"], str):
|
|
529
|
+
result.issues.append(ValidationIssue(
|
|
530
|
+
severity="error", field="email",
|
|
531
|
+
message=f"Expected str, got {type(raw['email']).__name__}",
|
|
532
|
+
))
|
|
533
|
+
|
|
534
|
+
# Optional: capauth_managed must be bool if present
|
|
535
|
+
if "capauth_managed" in raw and not isinstance(raw["capauth_managed"], bool):
|
|
536
|
+
result.issues.append(ValidationIssue(
|
|
537
|
+
severity="error", field="capauth_managed",
|
|
538
|
+
message=f"Expected bool, got {type(raw['capauth_managed']).__name__}",
|
|
539
|
+
))
|
|
540
|
+
|
|
541
|
+
return result
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# ---------------------------------------------------------------------------
|
|
545
|
+
# Soul blueprint JSON validator
|
|
546
|
+
# ---------------------------------------------------------------------------
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def validate_soul_blueprint_json(path: Path) -> FileValidationResult:
|
|
550
|
+
"""Validate a soul blueprint JSON file (base.json or installed/*.json).
|
|
551
|
+
|
|
552
|
+
Checks that ``name`` and ``display_name`` are present, and that
|
|
553
|
+
``core_traits`` / ``emotional_topology`` have the expected types.
|
|
554
|
+
|
|
555
|
+
Args:
|
|
556
|
+
path: Path to the soul JSON file.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
FileValidationResult with any errors/warnings found.
|
|
560
|
+
"""
|
|
561
|
+
config_name = f"soul/{path.name}"
|
|
562
|
+
result = FileValidationResult(config_name=config_name, file_path=path)
|
|
563
|
+
|
|
564
|
+
if not path.exists():
|
|
565
|
+
result.found = False
|
|
566
|
+
result.issues.append(ValidationIssue(
|
|
567
|
+
severity="warning", message="File not found",
|
|
568
|
+
))
|
|
569
|
+
return result
|
|
570
|
+
|
|
571
|
+
text = path.read_text(encoding="utf-8")
|
|
572
|
+
|
|
573
|
+
try:
|
|
574
|
+
raw: Any = json.loads(text)
|
|
575
|
+
except json.JSONDecodeError as exc:
|
|
576
|
+
result.issues.append(ValidationIssue(
|
|
577
|
+
severity="error",
|
|
578
|
+
message=f"JSON parse error: {exc.msg}",
|
|
579
|
+
line=exc.lineno,
|
|
580
|
+
))
|
|
581
|
+
return result
|
|
582
|
+
|
|
583
|
+
if not isinstance(raw, dict):
|
|
584
|
+
result.issues.append(ValidationIssue(
|
|
585
|
+
severity="error", message="Expected a JSON object", line=1,
|
|
586
|
+
))
|
|
587
|
+
return result
|
|
588
|
+
|
|
589
|
+
# Required: name + display_name
|
|
590
|
+
for req in ("name", "display_name"):
|
|
591
|
+
if req not in raw:
|
|
592
|
+
result.issues.append(ValidationIssue(
|
|
593
|
+
severity="error", field=req,
|
|
594
|
+
message=f"Required field '{req}' is missing",
|
|
595
|
+
))
|
|
596
|
+
elif not isinstance(raw[req], str) or not raw[req].strip():
|
|
597
|
+
result.issues.append(ValidationIssue(
|
|
598
|
+
severity="error", field=req,
|
|
599
|
+
message=f"'{req}' must be a non-empty string",
|
|
600
|
+
))
|
|
601
|
+
|
|
602
|
+
# core_traits: list
|
|
603
|
+
if "core_traits" in raw and not isinstance(raw["core_traits"], list):
|
|
604
|
+
result.issues.append(ValidationIssue(
|
|
605
|
+
severity="error", field="core_traits",
|
|
606
|
+
message=f"Expected list, got {type(raw['core_traits']).__name__}",
|
|
607
|
+
))
|
|
608
|
+
|
|
609
|
+
# emotional_topology: dict[str, float|int]
|
|
610
|
+
if "emotional_topology" in raw:
|
|
611
|
+
et = raw["emotional_topology"]
|
|
612
|
+
if not isinstance(et, dict):
|
|
613
|
+
result.issues.append(ValidationIssue(
|
|
614
|
+
severity="error", field="emotional_topology",
|
|
615
|
+
message=f"Expected dict, got {type(et).__name__}",
|
|
616
|
+
))
|
|
617
|
+
else:
|
|
618
|
+
for k, v in et.items():
|
|
619
|
+
if not isinstance(v, (int, float)):
|
|
620
|
+
result.issues.append(ValidationIssue(
|
|
621
|
+
severity="error",
|
|
622
|
+
field=f"emotional_topology.{k}",
|
|
623
|
+
message=f"Expected numeric value, got {type(v).__name__}",
|
|
624
|
+
))
|
|
625
|
+
|
|
626
|
+
return result
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
# ---------------------------------------------------------------------------
|
|
630
|
+
# Top-level entry point
|
|
631
|
+
# ---------------------------------------------------------------------------
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def validate_all(home: Path) -> ConfigValidationReport:
|
|
635
|
+
"""Validate all config files in an agent home directory.
|
|
636
|
+
|
|
637
|
+
Checks:
|
|
638
|
+
- ``{home}/config/consciousness.yaml``
|
|
639
|
+
- ``{home}/config/model_profiles.yaml``
|
|
640
|
+
- ``{home}/identity/identity.json``
|
|
641
|
+
- ``{home}/soul/base.json``
|
|
642
|
+
- ``{home}/soul/installed/*.json``
|
|
643
|
+
|
|
644
|
+
Files that do not exist are reported as warnings (not errors), since
|
|
645
|
+
missing files fall back to built-in defaults.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
home: Agent home directory (e.g. ``~/.skcapstone``).
|
|
649
|
+
|
|
650
|
+
Returns:
|
|
651
|
+
ConfigValidationReport aggregating all results.
|
|
652
|
+
"""
|
|
653
|
+
report = ConfigValidationReport()
|
|
654
|
+
config_dir = home / "config"
|
|
655
|
+
|
|
656
|
+
report.results.append(
|
|
657
|
+
validate_consciousness_yaml(config_dir / "consciousness.yaml")
|
|
658
|
+
)
|
|
659
|
+
report.results.append(
|
|
660
|
+
validate_model_profiles_yaml(config_dir / "model_profiles.yaml")
|
|
661
|
+
)
|
|
662
|
+
report.results.append(
|
|
663
|
+
validate_identity_json(home / "identity" / "identity.json")
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
soul_dir = home / "soul"
|
|
667
|
+
if soul_dir.exists():
|
|
668
|
+
report.results.append(validate_soul_blueprint_json(soul_dir / "base.json"))
|
|
669
|
+
installed_dir = soul_dir / "installed"
|
|
670
|
+
if installed_dir.exists():
|
|
671
|
+
for bp_file in sorted(installed_dir.glob("*.json")):
|
|
672
|
+
report.results.append(validate_soul_blueprint_json(bp_file))
|
|
673
|
+
|
|
674
|
+
return report
|
|
@@ -4,3 +4,31 @@ Platform connectors — windows into the sovereign agent.
|
|
|
4
4
|
Every connector talks to the same AgentRuntime.
|
|
5
5
|
The platform is just a viewport. The agent is the truth.
|
|
6
6
|
"""
|
|
7
|
+
|
|
8
|
+
from .base import (
|
|
9
|
+
FRAME_FORMAT,
|
|
10
|
+
FRAME_HEADER_SIZE,
|
|
11
|
+
ConnectorBackend,
|
|
12
|
+
ConnectorInfo,
|
|
13
|
+
ConnectorStatus,
|
|
14
|
+
ConnectorType,
|
|
15
|
+
UnixSocketConnector,
|
|
16
|
+
)
|
|
17
|
+
from .cursor import CursorConnector
|
|
18
|
+
from .registry import ConnectorRegistry
|
|
19
|
+
from .terminal import TerminalConnector
|
|
20
|
+
from .vscode import VSCodeConnector
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"FRAME_FORMAT",
|
|
24
|
+
"FRAME_HEADER_SIZE",
|
|
25
|
+
"ConnectorBackend",
|
|
26
|
+
"ConnectorInfo",
|
|
27
|
+
"ConnectorRegistry",
|
|
28
|
+
"ConnectorStatus",
|
|
29
|
+
"ConnectorType",
|
|
30
|
+
"CursorConnector",
|
|
31
|
+
"TerminalConnector",
|
|
32
|
+
"UnixSocketConnector",
|
|
33
|
+
"VSCodeConnector",
|
|
34
|
+
]
|