@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,522 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Team Deployment Engine — provider-agnostic orchestration.
|
|
3
|
+
|
|
4
|
+
Takes a BlueprintManifest and deploys it to the target infrastructure.
|
|
5
|
+
The engine doesn't care whether agents land on local processes, Proxmox
|
|
6
|
+
LXCs, Hetzner VMs, AWS, GCP, or Docker containers. Each provider
|
|
7
|
+
implements the same interface; the engine orchestrates the workflow.
|
|
8
|
+
|
|
9
|
+
Architecture follows best practices from:
|
|
10
|
+
- multi-agent-patterns: context isolation, supervisor coordination
|
|
11
|
+
- hosted-agents: pre-built images, warm pools, snapshot/restore
|
|
12
|
+
- memory-systems: filesystem-first, upgrade path to vector/graph
|
|
13
|
+
|
|
14
|
+
Deployment flow:
|
|
15
|
+
1. Validate blueprint + resolve dependencies
|
|
16
|
+
2. Provision infrastructure (provider-specific)
|
|
17
|
+
3. Configure each agent (soul, skills, memory, network)
|
|
18
|
+
4. Register in vault registry (skref)
|
|
19
|
+
5. Start health monitoring
|
|
20
|
+
6. Hand off to coordination layer (queen)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import logging
|
|
27
|
+
import time
|
|
28
|
+
from abc import ABC, abstractmethod
|
|
29
|
+
from datetime import datetime, timezone
|
|
30
|
+
from enum import Enum
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from typing import Any, Dict, List, Optional
|
|
33
|
+
|
|
34
|
+
from pydantic import BaseModel, Field
|
|
35
|
+
|
|
36
|
+
from .blueprints.schema import AgentRole, AgentSpec, BlueprintManifest, ProviderType
|
|
37
|
+
from .team_comms import TeamChannel, bootstrap_team_channel
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# Deployment state
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
class AgentStatus(str, Enum):
|
|
47
|
+
"""Runtime status of a deployed agent."""
|
|
48
|
+
|
|
49
|
+
PENDING = "pending"
|
|
50
|
+
PROVISIONING = "provisioning"
|
|
51
|
+
CONFIGURING = "configuring"
|
|
52
|
+
RUNNING = "running"
|
|
53
|
+
DEGRADED = "degraded"
|
|
54
|
+
STOPPED = "stopped"
|
|
55
|
+
FAILED = "failed"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DeployedAgent(BaseModel):
|
|
59
|
+
"""Runtime state of a single deployed agent instance."""
|
|
60
|
+
|
|
61
|
+
name: str
|
|
62
|
+
instance_id: str
|
|
63
|
+
blueprint_slug: str
|
|
64
|
+
agent_spec_key: str
|
|
65
|
+
status: AgentStatus = AgentStatus.PENDING
|
|
66
|
+
provider: ProviderType = ProviderType.LOCAL
|
|
67
|
+
host: Optional[str] = None
|
|
68
|
+
port: Optional[int] = None
|
|
69
|
+
pid: Optional[int] = None
|
|
70
|
+
container_id: Optional[str] = None
|
|
71
|
+
started_at: Optional[str] = None
|
|
72
|
+
last_heartbeat: Optional[str] = None
|
|
73
|
+
error: Optional[str] = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TeamDeployment(BaseModel):
|
|
77
|
+
"""Full state of a deployed team."""
|
|
78
|
+
|
|
79
|
+
deployment_id: str
|
|
80
|
+
blueprint_slug: str
|
|
81
|
+
team_name: str
|
|
82
|
+
provider: ProviderType
|
|
83
|
+
agents: Dict[str, DeployedAgent] = Field(default_factory=dict)
|
|
84
|
+
created_at: str = Field(
|
|
85
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
86
|
+
)
|
|
87
|
+
status: str = "deploying"
|
|
88
|
+
comms_channel: Optional[Any] = Field(
|
|
89
|
+
default=None,
|
|
90
|
+
exclude=True,
|
|
91
|
+
description="In-memory TeamChannel; not persisted to disk.",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# Provider interface (abstract base)
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
class ProviderBackend(ABC):
|
|
100
|
+
"""Abstract base for infrastructure providers.
|
|
101
|
+
|
|
102
|
+
Each provider (local, proxmox, hetzner, aws, gcp, docker) implements
|
|
103
|
+
these methods. The engine calls them in sequence during deployment.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
provider_type: ProviderType = ProviderType.LOCAL
|
|
107
|
+
|
|
108
|
+
@abstractmethod
|
|
109
|
+
def provision(
|
|
110
|
+
self,
|
|
111
|
+
agent_name: str,
|
|
112
|
+
spec: AgentSpec,
|
|
113
|
+
team_name: str,
|
|
114
|
+
) -> Dict[str, Any]:
|
|
115
|
+
"""Provision infrastructure for one agent.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
agent_name: Unique name for this agent instance.
|
|
119
|
+
spec: The agent specification from the blueprint.
|
|
120
|
+
team_name: The team this agent belongs to.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Dict with provider-specific details (host, port, pid, container_id, etc.)
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
@abstractmethod
|
|
127
|
+
def configure(
|
|
128
|
+
self,
|
|
129
|
+
agent_name: str,
|
|
130
|
+
spec: AgentSpec,
|
|
131
|
+
provision_result: Dict[str, Any],
|
|
132
|
+
) -> bool:
|
|
133
|
+
"""Configure an agent after provisioning (soul, skills, memory).
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
agent_name: The agent instance name.
|
|
137
|
+
spec: The agent specification.
|
|
138
|
+
provision_result: Output from provision().
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
True if configuration succeeded.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
@abstractmethod
|
|
145
|
+
def start(self, agent_name: str, provision_result: Dict[str, Any]) -> bool:
|
|
146
|
+
"""Start the agent process/container.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
agent_name: The agent instance name.
|
|
150
|
+
provision_result: Output from provision().
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
True if the agent started successfully.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def stop(self, agent_name: str, provision_result: Dict[str, Any]) -> bool:
|
|
158
|
+
"""Stop a running agent.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
agent_name: The agent instance name.
|
|
162
|
+
provision_result: Output from provision().
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
True if the agent stopped successfully.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
@abstractmethod
|
|
169
|
+
def destroy(self, agent_name: str, provision_result: Dict[str, Any]) -> bool:
|
|
170
|
+
"""Destroy agent infrastructure entirely.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
agent_name: The agent instance name.
|
|
174
|
+
provision_result: Output from provision().
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
True if destruction succeeded.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
@abstractmethod
|
|
181
|
+
def health_check(self, agent_name: str, provision_result: Dict[str, Any]) -> AgentStatus:
|
|
182
|
+
"""Check the health of a deployed agent.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
agent_name: The agent instance name.
|
|
186
|
+
provision_result: Output from provision().
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Current AgentStatus.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
# Deployment engine
|
|
195
|
+
# ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
class TeamEngine:
|
|
198
|
+
"""Orchestrates team deployment across any provider.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
home: Agent home directory.
|
|
202
|
+
provider: The backend to deploy to.
|
|
203
|
+
comms_root: Root directory for team comms channels. Defaults to
|
|
204
|
+
``<home>/comms``. Pass ``None`` to disable comms bootstrapping.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
def __init__(
|
|
208
|
+
self,
|
|
209
|
+
home: Optional[Path] = None,
|
|
210
|
+
provider: Optional[ProviderBackend] = None,
|
|
211
|
+
comms_root: Optional[Path] = None,
|
|
212
|
+
) -> None:
|
|
213
|
+
self._home = (home or Path("~/.skcapstone")).expanduser()
|
|
214
|
+
self._provider = provider
|
|
215
|
+
self._deployments_dir = self._home / "deployments"
|
|
216
|
+
self._deployments_dir.mkdir(parents=True, exist_ok=True)
|
|
217
|
+
# Reason: allow callers to disable comms by passing comms_root=None explicitly
|
|
218
|
+
self._comms_root: Optional[Path] = (
|
|
219
|
+
comms_root if comms_root is not None else self._home / "comms"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# ------------------------------------------------------------------
|
|
223
|
+
# Dependency resolution
|
|
224
|
+
# ------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
@staticmethod
|
|
227
|
+
def resolve_deploy_order(blueprint: BlueprintManifest) -> List[List[str]]:
|
|
228
|
+
"""Topological sort of agents by depends_on.
|
|
229
|
+
|
|
230
|
+
Returns a list of "waves" — agents in the same wave can deploy
|
|
231
|
+
in parallel; each wave must complete before the next starts.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
blueprint: The team blueprint.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
List of waves, each wave is a list of agent keys.
|
|
238
|
+
|
|
239
|
+
Raises:
|
|
240
|
+
ValueError: If circular dependencies are detected.
|
|
241
|
+
"""
|
|
242
|
+
agent_keys = set(blueprint.agents.keys())
|
|
243
|
+
remaining = dict(blueprint.agents)
|
|
244
|
+
waves: List[List[str]] = []
|
|
245
|
+
resolved: set = set()
|
|
246
|
+
|
|
247
|
+
max_iterations = len(agent_keys) + 1
|
|
248
|
+
iteration = 0
|
|
249
|
+
|
|
250
|
+
while remaining:
|
|
251
|
+
iteration += 1
|
|
252
|
+
if iteration > max_iterations:
|
|
253
|
+
unresolved = list(remaining.keys())
|
|
254
|
+
raise ValueError(
|
|
255
|
+
f"Circular dependency detected among: {unresolved}"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
wave = []
|
|
259
|
+
for key, spec in list(remaining.items()):
|
|
260
|
+
deps = set(spec.depends_on) & agent_keys
|
|
261
|
+
if deps <= resolved:
|
|
262
|
+
wave.append(key)
|
|
263
|
+
|
|
264
|
+
if not wave:
|
|
265
|
+
unresolved = list(remaining.keys())
|
|
266
|
+
raise ValueError(
|
|
267
|
+
f"Unresolvable dependencies for: {unresolved}"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
for key in wave:
|
|
271
|
+
del remaining[key]
|
|
272
|
+
resolved.add(key)
|
|
273
|
+
|
|
274
|
+
waves.append(wave)
|
|
275
|
+
|
|
276
|
+
return waves
|
|
277
|
+
|
|
278
|
+
# ------------------------------------------------------------------
|
|
279
|
+
# Deployment
|
|
280
|
+
# ------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
def deploy(
|
|
283
|
+
self,
|
|
284
|
+
blueprint: BlueprintManifest,
|
|
285
|
+
name: Optional[str] = None,
|
|
286
|
+
provider_override: Optional[ProviderType] = None,
|
|
287
|
+
) -> TeamDeployment:
|
|
288
|
+
"""Deploy a team from a blueprint.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
blueprint: The validated blueprint manifest.
|
|
292
|
+
name: Optional custom deployment name.
|
|
293
|
+
provider_override: Override the blueprint's default provider.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
TeamDeployment with the full state of all agents.
|
|
297
|
+
"""
|
|
298
|
+
deployment_id = f"{blueprint.slug}-{int(time.time())}"
|
|
299
|
+
team_name = name or blueprint.name
|
|
300
|
+
provider_type = provider_override or blueprint.default_provider
|
|
301
|
+
|
|
302
|
+
deployment = TeamDeployment(
|
|
303
|
+
deployment_id=deployment_id,
|
|
304
|
+
blueprint_slug=blueprint.slug,
|
|
305
|
+
team_name=team_name,
|
|
306
|
+
provider=provider_type,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
waves = self.resolve_deploy_order(blueprint)
|
|
310
|
+
logger.info(
|
|
311
|
+
"Deploying %s: %d agents in %d waves",
|
|
312
|
+
team_name, blueprint.agent_count, len(waves),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
for wave_idx, wave in enumerate(waves):
|
|
316
|
+
logger.info("Wave %d: %s", wave_idx + 1, wave)
|
|
317
|
+
|
|
318
|
+
for agent_key in wave:
|
|
319
|
+
spec = blueprint.agents[agent_key]
|
|
320
|
+
for instance_num in range(spec.count):
|
|
321
|
+
suffix = f"-{instance_num + 1}" if spec.count > 1 else ""
|
|
322
|
+
instance_name = f"{blueprint.slug}-{agent_key}{suffix}"
|
|
323
|
+
|
|
324
|
+
deployed = DeployedAgent(
|
|
325
|
+
name=instance_name,
|
|
326
|
+
instance_id=f"{deployment_id}/{instance_name}",
|
|
327
|
+
blueprint_slug=blueprint.slug,
|
|
328
|
+
agent_spec_key=agent_key,
|
|
329
|
+
provider=provider_type,
|
|
330
|
+
status=AgentStatus.PROVISIONING,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
if self._provider:
|
|
334
|
+
try:
|
|
335
|
+
result = self._provider.provision(
|
|
336
|
+
instance_name, spec, team_name,
|
|
337
|
+
)
|
|
338
|
+
deployed.host = result.get("host")
|
|
339
|
+
deployed.port = result.get("port")
|
|
340
|
+
deployed.pid = result.get("pid")
|
|
341
|
+
deployed.container_id = result.get("container_id")
|
|
342
|
+
|
|
343
|
+
deployed.status = AgentStatus.CONFIGURING
|
|
344
|
+
self._provider.configure(instance_name, spec, result)
|
|
345
|
+
|
|
346
|
+
deployed.status = AgentStatus.RUNNING
|
|
347
|
+
self._provider.start(instance_name, result)
|
|
348
|
+
deployed.started_at = datetime.now(
|
|
349
|
+
timezone.utc
|
|
350
|
+
).isoformat()
|
|
351
|
+
deployed.last_heartbeat = deployed.started_at
|
|
352
|
+
|
|
353
|
+
except Exception as exc:
|
|
354
|
+
deployed.status = AgentStatus.FAILED
|
|
355
|
+
deployed.error = str(exc)
|
|
356
|
+
logger.error(
|
|
357
|
+
"Failed to deploy %s: %s", instance_name, exc
|
|
358
|
+
)
|
|
359
|
+
else:
|
|
360
|
+
# Dry-run mode: no provider, just record the plan
|
|
361
|
+
deployed.status = AgentStatus.PENDING
|
|
362
|
+
deployed.host = "localhost"
|
|
363
|
+
|
|
364
|
+
deployment.agents[instance_name] = deployed
|
|
365
|
+
|
|
366
|
+
deployment.status = (
|
|
367
|
+
"running"
|
|
368
|
+
if all(
|
|
369
|
+
a.status == AgentStatus.RUNNING or a.status == AgentStatus.PENDING
|
|
370
|
+
for a in deployment.agents.values()
|
|
371
|
+
)
|
|
372
|
+
else "degraded"
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
self._save_deployment(deployment)
|
|
376
|
+
|
|
377
|
+
if self._comms_root is not None:
|
|
378
|
+
deployment.comms_channel = self._bootstrap_comms(
|
|
379
|
+
deployment, blueprint
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
return deployment
|
|
383
|
+
|
|
384
|
+
# ------------------------------------------------------------------
|
|
385
|
+
# Status / management
|
|
386
|
+
# ------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
def list_deployments(self) -> List[TeamDeployment]:
|
|
389
|
+
"""List all saved deployments.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
List of TeamDeployment objects.
|
|
393
|
+
"""
|
|
394
|
+
deployments = []
|
|
395
|
+
for f in sorted(self._deployments_dir.glob("*.json")):
|
|
396
|
+
try:
|
|
397
|
+
data = json.loads(f.read_text(encoding="utf-8"))
|
|
398
|
+
deployments.append(TeamDeployment(**data))
|
|
399
|
+
except Exception as exc:
|
|
400
|
+
logger.warning("Skipping %s: %s", f, exc)
|
|
401
|
+
return deployments
|
|
402
|
+
|
|
403
|
+
def get_deployment(self, deployment_id: str) -> Optional[TeamDeployment]:
|
|
404
|
+
"""Load a specific deployment by ID.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
deployment_id: The deployment identifier.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
TeamDeployment or None.
|
|
411
|
+
"""
|
|
412
|
+
path = self._deployments_dir / f"{deployment_id}.json"
|
|
413
|
+
if not path.exists():
|
|
414
|
+
return None
|
|
415
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
416
|
+
return TeamDeployment(**data)
|
|
417
|
+
|
|
418
|
+
def destroy_deployment(self, deployment_id: str) -> bool:
|
|
419
|
+
"""Destroy all agents in a deployment and remove state.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
deployment_id: The deployment to destroy.
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
True if all agents were destroyed.
|
|
426
|
+
"""
|
|
427
|
+
deployment = self.get_deployment(deployment_id)
|
|
428
|
+
if not deployment:
|
|
429
|
+
return False
|
|
430
|
+
|
|
431
|
+
all_ok = True
|
|
432
|
+
if self._provider:
|
|
433
|
+
for agent in deployment.agents.values():
|
|
434
|
+
try:
|
|
435
|
+
self._provider.destroy(
|
|
436
|
+
agent.name,
|
|
437
|
+
{"host": agent.host, "pid": agent.pid,
|
|
438
|
+
"container_id": agent.container_id},
|
|
439
|
+
)
|
|
440
|
+
except Exception as exc:
|
|
441
|
+
logger.error("Failed to destroy %s: %s", agent.name, exc)
|
|
442
|
+
all_ok = False
|
|
443
|
+
|
|
444
|
+
path = self._deployments_dir / f"{deployment_id}.json"
|
|
445
|
+
if path.exists():
|
|
446
|
+
path.unlink()
|
|
447
|
+
|
|
448
|
+
return all_ok
|
|
449
|
+
|
|
450
|
+
# ------------------------------------------------------------------
|
|
451
|
+
# Persistence
|
|
452
|
+
# ------------------------------------------------------------------
|
|
453
|
+
|
|
454
|
+
def _save_deployment(self, deployment: TeamDeployment) -> Path:
|
|
455
|
+
"""Save deployment state to disk.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
deployment: The deployment to persist.
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
Path to the saved JSON file.
|
|
462
|
+
"""
|
|
463
|
+
path = self._deployments_dir / f"{deployment.deployment_id}.json"
|
|
464
|
+
path.write_text(
|
|
465
|
+
json.dumps(deployment.model_dump(mode="json"), indent=2),
|
|
466
|
+
encoding="utf-8",
|
|
467
|
+
)
|
|
468
|
+
return path
|
|
469
|
+
|
|
470
|
+
def _bootstrap_comms(
|
|
471
|
+
self,
|
|
472
|
+
deployment: TeamDeployment,
|
|
473
|
+
blueprint: BlueprintManifest,
|
|
474
|
+
) -> TeamChannel:
|
|
475
|
+
"""Bootstrap SKComm file channel for all agents in a deployment.
|
|
476
|
+
|
|
477
|
+
Identifies the queen agent from the blueprint's coordination config
|
|
478
|
+
(falling back to any agent with role=manager) and provisions per-agent
|
|
479
|
+
inboxes plus a broadcast directory.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
deployment: The freshly created TeamDeployment.
|
|
483
|
+
blueprint: The blueprint manifest used for this deployment.
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
TeamChannel: The configured comms channel.
|
|
487
|
+
"""
|
|
488
|
+
agent_names = list(deployment.agents.keys())
|
|
489
|
+
|
|
490
|
+
# Determine the queen: prefer coordination config, then role=manager
|
|
491
|
+
queen: Optional[str] = None
|
|
492
|
+
configured_queen = blueprint.coordination.queen
|
|
493
|
+
if configured_queen:
|
|
494
|
+
# Find the deployed instance that matches this spec key
|
|
495
|
+
for inst_name in agent_names:
|
|
496
|
+
agent = deployment.agents[inst_name]
|
|
497
|
+
if agent.agent_spec_key == configured_queen:
|
|
498
|
+
queen = inst_name
|
|
499
|
+
break
|
|
500
|
+
|
|
501
|
+
if queen is None:
|
|
502
|
+
for inst_name in agent_names:
|
|
503
|
+
agent = deployment.agents[inst_name]
|
|
504
|
+
spec = blueprint.agents.get(agent.agent_spec_key)
|
|
505
|
+
if spec and spec.role == AgentRole.MANAGER:
|
|
506
|
+
queen = inst_name
|
|
507
|
+
break
|
|
508
|
+
|
|
509
|
+
assert self._comms_root is not None # guarded by caller
|
|
510
|
+
channel = bootstrap_team_channel(
|
|
511
|
+
team_slug=deployment.deployment_id,
|
|
512
|
+
agent_names=agent_names,
|
|
513
|
+
comms_root=self._comms_root,
|
|
514
|
+
queen=queen,
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
logger.info(
|
|
518
|
+
"Comms channel ready for deployment '%s' (queen=%s)",
|
|
519
|
+
deployment.deployment_id,
|
|
520
|
+
queen or "none",
|
|
521
|
+
)
|
|
522
|
+
return channel
|