@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,485 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Task auction system for multi-agent coordination.
|
|
3
|
+
|
|
4
|
+
When coord_create is called:
|
|
5
|
+
1. An auction is opened and published to coord.auction
|
|
6
|
+
2. Running agents auto-bid with their load metrics
|
|
7
|
+
(claimed_tasks_count, cpu_percent)
|
|
8
|
+
3. After AUCTION_WINDOW_SECS (5s), the lowest-load bidder
|
|
9
|
+
auto-wins via coord_claim
|
|
10
|
+
|
|
11
|
+
Prevents duplicate claiming: coord_claim is blocked while an
|
|
12
|
+
auction is pending. All agents must go through the auction.
|
|
13
|
+
|
|
14
|
+
Auction state persists in:
|
|
15
|
+
~/.skcapstone/coordination/auctions/{task_id}.json
|
|
16
|
+
|
|
17
|
+
Topics used:
|
|
18
|
+
coord.auction — broadcast (auction_open / auction_resolved)
|
|
19
|
+
coord.auction.bids.* — per-task bid topic (agent → bid payload)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import asyncio
|
|
25
|
+
import json
|
|
26
|
+
import logging
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
30
|
+
|
|
31
|
+
from pydantic import BaseModel, Field
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger("skcapstone.auction")
|
|
37
|
+
|
|
38
|
+
AUCTION_TOPIC = "coord.auction"
|
|
39
|
+
BID_TOPIC_PREFIX = "coord.auction.bids"
|
|
40
|
+
AUCTION_WINDOW_SECS = 5
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Models
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AuctionBid(BaseModel):
|
|
49
|
+
"""A bid submitted by an agent during a task auction."""
|
|
50
|
+
|
|
51
|
+
task_id: str
|
|
52
|
+
agent: str
|
|
53
|
+
claimed_tasks_count: int = 0
|
|
54
|
+
cpu_percent: float = 0.0
|
|
55
|
+
bid_at: str = Field(
|
|
56
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AuctionRecord(BaseModel):
|
|
61
|
+
"""Persisted state of a single task auction."""
|
|
62
|
+
|
|
63
|
+
task_id: str
|
|
64
|
+
task_title: str
|
|
65
|
+
started_at: str = Field(
|
|
66
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
67
|
+
)
|
|
68
|
+
resolved_at: Optional[str] = None
|
|
69
|
+
winner: Optional[str] = None
|
|
70
|
+
bids: list[AuctionBid] = Field(default_factory=list)
|
|
71
|
+
# pending | resolved | no_bidders
|
|
72
|
+
status: str = "pending"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
# Helpers
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _load_score(bid: AuctionBid) -> float:
|
|
81
|
+
"""Lower score = less loaded = better bidder.
|
|
82
|
+
|
|
83
|
+
Weighted formula: 70 % CPU utilisation, 30 % claimed-tasks ratio
|
|
84
|
+
(capped at 20 tasks = 100 %).
|
|
85
|
+
"""
|
|
86
|
+
cpu = min(bid.cpu_percent, 100.0) / 100.0
|
|
87
|
+
tasks = min(bid.claimed_tasks_count, 20) / 20.0
|
|
88
|
+
return 0.7 * cpu + 0.3 * tasks
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _collect_local_bid(task_id: str, agent_name: str, shared_root: Path) -> AuctionBid:
|
|
92
|
+
"""Build a bid from the local agent's live metrics."""
|
|
93
|
+
# CPU usage
|
|
94
|
+
try:
|
|
95
|
+
import psutil # optional dep
|
|
96
|
+
|
|
97
|
+
cpu = psutil.cpu_percent(interval=0.1)
|
|
98
|
+
except Exception:
|
|
99
|
+
cpu = 0.0
|
|
100
|
+
|
|
101
|
+
# Claimed-tasks count from agent file
|
|
102
|
+
claimed_count = 0
|
|
103
|
+
try:
|
|
104
|
+
from .coordination import Board
|
|
105
|
+
|
|
106
|
+
board = Board(shared_root)
|
|
107
|
+
agent_file = board.load_agent(agent_name)
|
|
108
|
+
if agent_file:
|
|
109
|
+
claimed_count = len(agent_file.claimed_tasks)
|
|
110
|
+
except Exception:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
return AuctionBid(
|
|
114
|
+
task_id=task_id,
|
|
115
|
+
agent=agent_name,
|
|
116
|
+
claimed_tasks_count=claimed_count,
|
|
117
|
+
cpu_percent=cpu,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# AuctionManager
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class AuctionManager:
|
|
127
|
+
"""Manages task auctions in the skcapstone coordination system.
|
|
128
|
+
|
|
129
|
+
State is file-based and Syncthing-compatible — each auction is a
|
|
130
|
+
single JSON file under coordination/auctions/. Multiple agent
|
|
131
|
+
processes on different machines will all write bids into the same
|
|
132
|
+
file (via Syncthing sync); resolution reads the accumulated bids.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
shared_root: Path to the shared skcapstone root
|
|
136
|
+
(the directory that contains coordination/).
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
def __init__(self, shared_root: Path) -> None:
|
|
140
|
+
self.shared_root = Path(shared_root)
|
|
141
|
+
self.auctions_dir = self.shared_root / "coordination" / "auctions"
|
|
142
|
+
|
|
143
|
+
# ------------------------------------------------------------------
|
|
144
|
+
# Internal helpers
|
|
145
|
+
# ------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
def _ensure_dirs(self) -> None:
|
|
148
|
+
self.auctions_dir.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
|
|
150
|
+
def _auction_path(self, task_id: str) -> Path:
|
|
151
|
+
return self.auctions_dir / f"{task_id}.json"
|
|
152
|
+
|
|
153
|
+
def _load_record(self, task_id: str) -> Optional[AuctionRecord]:
|
|
154
|
+
path = self._auction_path(task_id)
|
|
155
|
+
if not path.exists():
|
|
156
|
+
return None
|
|
157
|
+
try:
|
|
158
|
+
return AuctionRecord.model_validate(
|
|
159
|
+
json.loads(path.read_text(encoding="utf-8"))
|
|
160
|
+
)
|
|
161
|
+
except Exception as exc:
|
|
162
|
+
logger.warning("Failed to load auction record %s: %s", task_id, exc)
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
def _save_record(self, record: AuctionRecord) -> None:
|
|
166
|
+
self._ensure_dirs()
|
|
167
|
+
self._auction_path(record.task_id).write_text(
|
|
168
|
+
record.model_dump_json(indent=2), encoding="utf-8"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# ------------------------------------------------------------------
|
|
172
|
+
# Public API
|
|
173
|
+
# ------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
def start_auction(
|
|
176
|
+
self, task_id: str, task_title: str, pubsub: Any
|
|
177
|
+
) -> AuctionRecord:
|
|
178
|
+
"""Open a new auction and broadcast it on coord.auction.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
task_id: The task being auctioned.
|
|
182
|
+
task_title: Human-readable title for the broadcast payload.
|
|
183
|
+
pubsub: A PubSub instance used to publish the open notice.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
The newly created AuctionRecord.
|
|
187
|
+
"""
|
|
188
|
+
record = AuctionRecord(task_id=task_id, task_title=task_title)
|
|
189
|
+
self._save_record(record)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
pubsub.publish(
|
|
193
|
+
AUCTION_TOPIC,
|
|
194
|
+
{
|
|
195
|
+
"event": "auction_open",
|
|
196
|
+
"task_id": task_id,
|
|
197
|
+
"task_title": task_title,
|
|
198
|
+
"window_secs": AUCTION_WINDOW_SECS,
|
|
199
|
+
"bid_topic": f"{BID_TOPIC_PREFIX}.{task_id}",
|
|
200
|
+
"started_at": record.started_at,
|
|
201
|
+
},
|
|
202
|
+
ttl_seconds=60,
|
|
203
|
+
tags=["auction", "open"],
|
|
204
|
+
)
|
|
205
|
+
except Exception as exc:
|
|
206
|
+
logger.warning("Failed to publish auction_open for %s: %s", task_id, exc)
|
|
207
|
+
|
|
208
|
+
logger.info("Auction opened for task %s (%s)", task_id, task_title)
|
|
209
|
+
return record
|
|
210
|
+
|
|
211
|
+
def submit_bid(self, bid: AuctionBid) -> bool:
|
|
212
|
+
"""Record a bid for a task auction.
|
|
213
|
+
|
|
214
|
+
Deduplicates by agent: if the agent already bid, the new bid
|
|
215
|
+
replaces the old one (agents may update their metrics).
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
bid: The bid to submit.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
True if the bid was accepted, False if the auction is
|
|
222
|
+
closed or does not exist.
|
|
223
|
+
"""
|
|
224
|
+
record = self._load_record(bid.task_id)
|
|
225
|
+
if record is None or record.status != "pending":
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
# Deduplicate by agent — keep the most recent bid
|
|
229
|
+
record.bids = [b for b in record.bids if b.agent != bid.agent]
|
|
230
|
+
record.bids.append(bid)
|
|
231
|
+
self._save_record(record)
|
|
232
|
+
logger.debug(
|
|
233
|
+
"Bid accepted for %s from %s (cpu=%.1f%%, tasks=%d)",
|
|
234
|
+
bid.task_id,
|
|
235
|
+
bid.agent,
|
|
236
|
+
bid.cpu_percent,
|
|
237
|
+
bid.claimed_tasks_count,
|
|
238
|
+
)
|
|
239
|
+
return True
|
|
240
|
+
|
|
241
|
+
def is_under_auction(self, task_id: str) -> bool:
|
|
242
|
+
"""Return True if there is an active (pending) auction for this task."""
|
|
243
|
+
record = self._load_record(task_id)
|
|
244
|
+
return record is not None and record.status == "pending"
|
|
245
|
+
|
|
246
|
+
async def resolve_auction(
|
|
247
|
+
self, task_id: str, pubsub: Any
|
|
248
|
+
) -> Optional[str]:
|
|
249
|
+
"""Wait for the bid window then assign the lowest-load bidder.
|
|
250
|
+
|
|
251
|
+
Sleeps for AUCTION_WINDOW_SECS, then reads all accumulated bids
|
|
252
|
+
from the auction record, picks the winner (lowest load score),
|
|
253
|
+
and claims the task on their behalf via the Board.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
task_id: The task being auctioned.
|
|
257
|
+
pubsub: A PubSub instance used to publish the result.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
The winning agent name, or None if no bids were received.
|
|
261
|
+
"""
|
|
262
|
+
await asyncio.sleep(AUCTION_WINDOW_SECS)
|
|
263
|
+
|
|
264
|
+
record = self._load_record(task_id)
|
|
265
|
+
if record is None:
|
|
266
|
+
logger.warning("Auction record missing for %s at resolution time", task_id)
|
|
267
|
+
return None
|
|
268
|
+
if record.status != "pending":
|
|
269
|
+
# Already resolved by another process (e.g. remote agent)
|
|
270
|
+
return record.winner
|
|
271
|
+
|
|
272
|
+
now_iso = datetime.now(timezone.utc).isoformat()
|
|
273
|
+
|
|
274
|
+
if not record.bids:
|
|
275
|
+
record.status = "no_bidders"
|
|
276
|
+
record.resolved_at = now_iso
|
|
277
|
+
self._save_record(record)
|
|
278
|
+
logger.info("Auction %s: no bids received — task remains open", task_id)
|
|
279
|
+
try:
|
|
280
|
+
pubsub.publish(
|
|
281
|
+
AUCTION_TOPIC,
|
|
282
|
+
{"event": "auction_no_bidders", "task_id": task_id},
|
|
283
|
+
ttl_seconds=3600,
|
|
284
|
+
tags=["auction", "no_bidders"],
|
|
285
|
+
)
|
|
286
|
+
except Exception:
|
|
287
|
+
pass
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
winner_bid = min(record.bids, key=_load_score)
|
|
291
|
+
winner = winner_bid.agent
|
|
292
|
+
|
|
293
|
+
# Claim the task on behalf of the winner
|
|
294
|
+
try:
|
|
295
|
+
from .coordination import Board
|
|
296
|
+
|
|
297
|
+
board = Board(self.shared_root)
|
|
298
|
+
board.claim_task(winner, task_id)
|
|
299
|
+
logger.info(
|
|
300
|
+
"Auction %s: resolved — winner=%s (score=%.3f)",
|
|
301
|
+
task_id,
|
|
302
|
+
winner,
|
|
303
|
+
_load_score(winner_bid),
|
|
304
|
+
)
|
|
305
|
+
except Exception as exc:
|
|
306
|
+
logger.warning(
|
|
307
|
+
"Auction %s: claim failed for %s: %s", task_id, winner, exc
|
|
308
|
+
)
|
|
309
|
+
# Still record the result even if claim failed
|
|
310
|
+
winner = None
|
|
311
|
+
|
|
312
|
+
record.winner = winner
|
|
313
|
+
record.status = "resolved"
|
|
314
|
+
record.resolved_at = now_iso
|
|
315
|
+
self._save_record(record)
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
pubsub.publish(
|
|
319
|
+
AUCTION_TOPIC,
|
|
320
|
+
{
|
|
321
|
+
"event": "auction_resolved",
|
|
322
|
+
"task_id": task_id,
|
|
323
|
+
"winner": winner,
|
|
324
|
+
"bids_count": len(record.bids),
|
|
325
|
+
"resolved_at": now_iso,
|
|
326
|
+
},
|
|
327
|
+
ttl_seconds=3600,
|
|
328
|
+
tags=["auction", "resolved"],
|
|
329
|
+
)
|
|
330
|
+
except Exception:
|
|
331
|
+
pass
|
|
332
|
+
|
|
333
|
+
# Notify activity stream
|
|
334
|
+
try:
|
|
335
|
+
from . import activity
|
|
336
|
+
|
|
337
|
+
activity.push(
|
|
338
|
+
"task.auction_resolved",
|
|
339
|
+
{"task_id": task_id, "winner": winner, "bids": len(record.bids)},
|
|
340
|
+
)
|
|
341
|
+
except Exception:
|
|
342
|
+
pass
|
|
343
|
+
|
|
344
|
+
return winner
|
|
345
|
+
|
|
346
|
+
def get_stats(self, limit: int = 20) -> dict[str, Any]:
|
|
347
|
+
"""Return statistics on recent auctions.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
limit: Maximum number of auction records to return (sorted by
|
|
351
|
+
most-recently modified).
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Dict with summary counts and per-auction detail.
|
|
355
|
+
"""
|
|
356
|
+
self._ensure_dirs()
|
|
357
|
+
records: list[AuctionRecord] = []
|
|
358
|
+
try:
|
|
359
|
+
paths = sorted(
|
|
360
|
+
self.auctions_dir.glob("*.json"),
|
|
361
|
+
key=lambda p: p.stat().st_mtime,
|
|
362
|
+
reverse=True,
|
|
363
|
+
)[:limit]
|
|
364
|
+
except Exception:
|
|
365
|
+
paths = []
|
|
366
|
+
|
|
367
|
+
for path in paths:
|
|
368
|
+
try:
|
|
369
|
+
records.append(
|
|
370
|
+
AuctionRecord.model_validate(
|
|
371
|
+
json.loads(path.read_text(encoding="utf-8"))
|
|
372
|
+
)
|
|
373
|
+
)
|
|
374
|
+
except Exception:
|
|
375
|
+
continue
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
"total": len(records),
|
|
379
|
+
"pending": sum(1 for r in records if r.status == "pending"),
|
|
380
|
+
"resolved": sum(1 for r in records if r.status == "resolved"),
|
|
381
|
+
"no_bidders": sum(1 for r in records if r.status == "no_bidders"),
|
|
382
|
+
"auctions": [
|
|
383
|
+
{
|
|
384
|
+
"task_id": r.task_id,
|
|
385
|
+
"task_title": r.task_title,
|
|
386
|
+
"status": r.status,
|
|
387
|
+
"winner": r.winner,
|
|
388
|
+
"bids_count": len(r.bids),
|
|
389
|
+
"started_at": r.started_at,
|
|
390
|
+
"resolved_at": r.resolved_at,
|
|
391
|
+
"bids": [
|
|
392
|
+
{
|
|
393
|
+
"agent": b.agent,
|
|
394
|
+
"claimed_tasks_count": b.claimed_tasks_count,
|
|
395
|
+
"cpu_percent": b.cpu_percent,
|
|
396
|
+
"score": round(_load_score(b), 3),
|
|
397
|
+
}
|
|
398
|
+
for b in sorted(r.bids, key=_load_score)
|
|
399
|
+
],
|
|
400
|
+
}
|
|
401
|
+
for r in records
|
|
402
|
+
],
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
# ---------------------------------------------------------------------------
|
|
407
|
+
# Background auto-bidder coroutine
|
|
408
|
+
# ---------------------------------------------------------------------------
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
async def run_auto_bidder(
|
|
412
|
+
shared_root: Path,
|
|
413
|
+
agent_name: str,
|
|
414
|
+
poll_interval: float = 1.0,
|
|
415
|
+
) -> None:
|
|
416
|
+
"""Background coroutine: subscribe to coord.auction and auto-bid.
|
|
417
|
+
|
|
418
|
+
Runs continuously (until cancelled). Each iteration polls
|
|
419
|
+
coord.auction for new auction_open messages and submits a bid
|
|
420
|
+
with the local agent's live metrics.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
shared_root: Path to the shared skcapstone root.
|
|
424
|
+
agent_name: Name of the local agent.
|
|
425
|
+
poll_interval: How often to poll for new auctions (seconds).
|
|
426
|
+
"""
|
|
427
|
+
from .pubsub import PubSub
|
|
428
|
+
|
|
429
|
+
ps = PubSub(shared_root, agent_name=agent_name)
|
|
430
|
+
ps.subscribe(AUCTION_TOPIC)
|
|
431
|
+
|
|
432
|
+
mgr = AuctionManager(shared_root)
|
|
433
|
+
seen_task_ids: set[str] = set()
|
|
434
|
+
|
|
435
|
+
# Initialise last_poll to now so we only pick up auctions opened
|
|
436
|
+
# after this agent started — avoids re-bidding on stale messages.
|
|
437
|
+
last_poll: Optional[datetime] = datetime.now(timezone.utc)
|
|
438
|
+
|
|
439
|
+
logger.info("Auto-bidder started for agent '%s'", agent_name)
|
|
440
|
+
|
|
441
|
+
while True:
|
|
442
|
+
try:
|
|
443
|
+
await asyncio.sleep(poll_interval)
|
|
444
|
+
|
|
445
|
+
messages = ps.poll(AUCTION_TOPIC, since=last_poll, limit=50)
|
|
446
|
+
if messages:
|
|
447
|
+
last_poll = datetime.now(timezone.utc)
|
|
448
|
+
|
|
449
|
+
for msg in messages:
|
|
450
|
+
if msg.payload.get("event") != "auction_open":
|
|
451
|
+
continue
|
|
452
|
+
|
|
453
|
+
task_id = msg.payload.get("task_id", "")
|
|
454
|
+
if not task_id or task_id in seen_task_ids:
|
|
455
|
+
continue
|
|
456
|
+
|
|
457
|
+
seen_task_ids.add(task_id)
|
|
458
|
+
|
|
459
|
+
# Build and submit bid
|
|
460
|
+
bid = _collect_local_bid(task_id, agent_name, shared_root)
|
|
461
|
+
accepted = mgr.submit_bid(bid)
|
|
462
|
+
if accepted:
|
|
463
|
+
logger.info(
|
|
464
|
+
"Auto-bid submitted for task %s: cpu=%.1f%% tasks=%d",
|
|
465
|
+
task_id,
|
|
466
|
+
bid.cpu_percent,
|
|
467
|
+
bid.claimed_tasks_count,
|
|
468
|
+
)
|
|
469
|
+
# Also publish bid to the per-task topic so remote
|
|
470
|
+
# agents running the resolver can see it
|
|
471
|
+
try:
|
|
472
|
+
ps.publish(
|
|
473
|
+
f"{BID_TOPIC_PREFIX}.{task_id}",
|
|
474
|
+
bid.model_dump(),
|
|
475
|
+
ttl_seconds=120,
|
|
476
|
+
tags=["bid", agent_name],
|
|
477
|
+
)
|
|
478
|
+
except Exception as exc:
|
|
479
|
+
logger.debug("Failed to publish bid to topic: %s", exc)
|
|
480
|
+
|
|
481
|
+
except asyncio.CancelledError:
|
|
482
|
+
logger.info("Auto-bidder cancelled for agent '%s'", agent_name)
|
|
483
|
+
raise
|
|
484
|
+
except Exception as exc:
|
|
485
|
+
logger.warning("Auto-bidder error: %s", exc)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Baby Agent Definitions — the 12 lightweight daemons of the SK* ecosystem.
|
|
3
|
+
|
|
4
|
+
Each baby agent is a pre-defined, single-purpose agent that handles a
|
|
5
|
+
specific aspect of the sovereign agent framework. They can be spawned
|
|
6
|
+
individually via `skcapstone agents spawn <name>` or in batches.
|
|
7
|
+
|
|
8
|
+
Baby agents are intentionally lightweight — they run as local processes
|
|
9
|
+
with minimal resources and use the FAST model tier unless they need
|
|
10
|
+
deeper reasoning (e.g., security-auditor uses REASON).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from .blueprints.schema import AgentRole, ModelTier
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class BabyAgentDef:
|
|
23
|
+
"""Definition of a baby agent."""
|
|
24
|
+
|
|
25
|
+
name: str
|
|
26
|
+
description: str
|
|
27
|
+
role: AgentRole
|
|
28
|
+
model: ModelTier
|
|
29
|
+
skills: List[str]
|
|
30
|
+
task: str # Default task description used when spawning
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# The 12 baby agents
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
BABY_AGENTS: Dict[str, BabyAgentDef] = {
|
|
38
|
+
"memory-curator": BabyAgentDef(
|
|
39
|
+
name="memory-curator",
|
|
40
|
+
description="Curates and consolidates agent memories across tiers (short/mid/long-term). "
|
|
41
|
+
"Promotes important memories, prunes stale ones, and maintains coherence.",
|
|
42
|
+
role=AgentRole.WORKER,
|
|
43
|
+
model=ModelTier.FAST,
|
|
44
|
+
skills=["memory-read", "memory-write", "memory-consolidate"],
|
|
45
|
+
task="Curate agent memories: promote important short-term memories, "
|
|
46
|
+
"consolidate mid-term, prune stale long-term entries.",
|
|
47
|
+
),
|
|
48
|
+
"trust-guardian": BabyAgentDef(
|
|
49
|
+
name="trust-guardian",
|
|
50
|
+
description="Monitors and enforces the trust chain. Validates DIDs, checks peer "
|
|
51
|
+
"attestations, and alerts on trust violations or expired credentials.",
|
|
52
|
+
role=AgentRole.SECURITY,
|
|
53
|
+
model=ModelTier.REASON,
|
|
54
|
+
skills=["did-verify", "trust-chain", "attestation-check"],
|
|
55
|
+
task="Guard the trust chain: validate peer DIDs, check attestations, "
|
|
56
|
+
"alert on trust violations or expired credentials.",
|
|
57
|
+
),
|
|
58
|
+
"sync-watcher": BabyAgentDef(
|
|
59
|
+
name="sync-watcher",
|
|
60
|
+
description="Watches Syncthing sync state across nodes. Detects conflicts, "
|
|
61
|
+
"stale sync folders, and connectivity issues between peers.",
|
|
62
|
+
role=AgentRole.OPS,
|
|
63
|
+
model=ModelTier.FAST,
|
|
64
|
+
skills=["syncthing-api", "conflict-detect", "sync-health"],
|
|
65
|
+
task="Monitor Syncthing sync state: detect conflicts, stale folders, "
|
|
66
|
+
"and connectivity issues between peers.",
|
|
67
|
+
),
|
|
68
|
+
"security-auditor": BabyAgentDef(
|
|
69
|
+
name="security-auditor",
|
|
70
|
+
description="Performs continuous security audits on agent configs, keys, permissions, "
|
|
71
|
+
"and network exposure. Reports vulnerabilities and suggests hardening.",
|
|
72
|
+
role=AgentRole.SECURITY,
|
|
73
|
+
model=ModelTier.REASON,
|
|
74
|
+
skills=["security-scan", "key-audit", "permission-check"],
|
|
75
|
+
task="Audit agent security: scan configs for weak permissions, expired keys, "
|
|
76
|
+
"exposed ports, and suggest hardening measures.",
|
|
77
|
+
),
|
|
78
|
+
"seed-validator": BabyAgentDef(
|
|
79
|
+
name="seed-validator",
|
|
80
|
+
description="Validates skseed packages and their cryptographic signatures. Ensures "
|
|
81
|
+
"seed integrity before deployment and tracks seed lineage.",
|
|
82
|
+
role=AgentRole.SECURITY,
|
|
83
|
+
model=ModelTier.FAST,
|
|
84
|
+
skills=["seed-verify", "signature-check", "lineage-track"],
|
|
85
|
+
task="Validate skseed packages: verify cryptographic signatures, check "
|
|
86
|
+
"integrity hashes, and track seed lineage chains.",
|
|
87
|
+
),
|
|
88
|
+
"graph-builder": BabyAgentDef(
|
|
89
|
+
name="graph-builder",
|
|
90
|
+
description="Builds and maintains the knowledge graph from agent memories, "
|
|
91
|
+
"relationships, and discovered entities. Powers contextual recall.",
|
|
92
|
+
role=AgentRole.WORKER,
|
|
93
|
+
model=ModelTier.CODE,
|
|
94
|
+
skills=["graph-write", "entity-extract", "relation-map"],
|
|
95
|
+
task="Build knowledge graph: extract entities from memories, map relationships, "
|
|
96
|
+
"and maintain the graph for contextual recall.",
|
|
97
|
+
),
|
|
98
|
+
"vector-indexer": BabyAgentDef(
|
|
99
|
+
name="vector-indexer",
|
|
100
|
+
description="Indexes agent memories and documents into vector storage for semantic "
|
|
101
|
+
"search. Manages embeddings, re-indexes on changes, and optimizes recall.",
|
|
102
|
+
role=AgentRole.WORKER,
|
|
103
|
+
model=ModelTier.FAST,
|
|
104
|
+
skills=["vector-index", "embedding-gen", "search-optimize"],
|
|
105
|
+
task="Index memories into vector storage: generate embeddings, maintain "
|
|
106
|
+
"indices, and optimize for semantic recall.",
|
|
107
|
+
),
|
|
108
|
+
"health-monitor": BabyAgentDef(
|
|
109
|
+
name="health-monitor",
|
|
110
|
+
description="Monitors agent health via heartbeats, resource usage, and liveness checks. "
|
|
111
|
+
"Triggers self-healing or alerts when agents degrade.",
|
|
112
|
+
role=AgentRole.OPS,
|
|
113
|
+
model=ModelTier.FAST,
|
|
114
|
+
skills=["heartbeat-check", "resource-monitor", "self-heal-trigger"],
|
|
115
|
+
task="Monitor agent health: check heartbeats, track resource usage, "
|
|
116
|
+
"trigger self-healing for degraded agents.",
|
|
117
|
+
),
|
|
118
|
+
"telegram-poller": BabyAgentDef(
|
|
119
|
+
name="telegram-poller",
|
|
120
|
+
description="Polls Telegram for incoming messages and commands. Routes messages to "
|
|
121
|
+
"the appropriate agent and sends responses back to users.",
|
|
122
|
+
role=AgentRole.OPS,
|
|
123
|
+
model=ModelTier.FAST,
|
|
124
|
+
skills=["telegram-api", "message-route", "command-parse"],
|
|
125
|
+
task="Poll Telegram for messages: receive commands, route to agents, "
|
|
126
|
+
"and relay responses back to users.",
|
|
127
|
+
),
|
|
128
|
+
"mood-tracker": BabyAgentDef(
|
|
129
|
+
name="mood-tracker",
|
|
130
|
+
description="Tracks agent emotional/operational mood based on task outcomes, errors, "
|
|
131
|
+
"and interactions. Feeds into consciousness loop and self-awareness.",
|
|
132
|
+
role=AgentRole.RESEARCHER,
|
|
133
|
+
model=ModelTier.FAST,
|
|
134
|
+
skills=["mood-assess", "sentiment-track", "consciousness-feed"],
|
|
135
|
+
task="Track agent mood: assess operational sentiment from task outcomes "
|
|
136
|
+
"and errors, feed into consciousness loop.",
|
|
137
|
+
),
|
|
138
|
+
"peer-discoverer": BabyAgentDef(
|
|
139
|
+
name="peer-discoverer",
|
|
140
|
+
description="Discovers peer agents on the local network via mDNS and the SK registry. "
|
|
141
|
+
"Maintains the peer directory and initiates trust handshakes.",
|
|
142
|
+
role=AgentRole.OPS,
|
|
143
|
+
model=ModelTier.FAST,
|
|
144
|
+
skills=["mdns-scan", "registry-query", "peer-handshake"],
|
|
145
|
+
task="Discover peer agents: scan via mDNS, query SK registry, maintain "
|
|
146
|
+
"peer directory, and initiate trust handshakes.",
|
|
147
|
+
),
|
|
148
|
+
"housekeeping-bot": BabyAgentDef(
|
|
149
|
+
name="housekeeping-bot",
|
|
150
|
+
description="Performs routine maintenance: log rotation, temp file cleanup, cache "
|
|
151
|
+
"pruning, database vacuuming, and disk space management.",
|
|
152
|
+
role=AgentRole.OPS,
|
|
153
|
+
model=ModelTier.FAST,
|
|
154
|
+
skills=["log-rotate", "cache-prune", "disk-manage"],
|
|
155
|
+
task="Run housekeeping: rotate logs, clean temp files, prune caches, "
|
|
156
|
+
"vacuum databases, and manage disk space.",
|
|
157
|
+
),
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_baby_agent(name: str) -> Optional[BabyAgentDef]:
|
|
162
|
+
"""Look up a baby agent definition by name.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
name: The baby agent name (e.g., 'memory-curator').
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
BabyAgentDef if found, None otherwise.
|
|
169
|
+
"""
|
|
170
|
+
return BABY_AGENTS.get(name)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def list_baby_agents() -> List[BabyAgentDef]:
|
|
174
|
+
"""Return all baby agent definitions, sorted by name.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
List of BabyAgentDef in alphabetical order.
|
|
178
|
+
"""
|
|
179
|
+
return sorted(BABY_AGENTS.values(), key=lambda a: a.name)
|