@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,476 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Team Communications — SKComm/SKChat wiring for deployed agent teams.
|
|
3
|
+
|
|
4
|
+
Bootstraps a local file-based SKComm channel for each deployed team so that
|
|
5
|
+
agents can message each other without external infrastructure. Each agent gets
|
|
6
|
+
its own inbox directory; messages are plain JSON envelopes written atomically
|
|
7
|
+
to the recipient's inbox folder.
|
|
8
|
+
|
|
9
|
+
Directory layout inside a team's comms directory:
|
|
10
|
+
<comms_root>/<team_slug>/
|
|
11
|
+
├── <agent_name>/
|
|
12
|
+
│ ├── inbox/ # Incoming .skc.json envelope files
|
|
13
|
+
│ └── archive/ # Processed envelopes (kept for audit)
|
|
14
|
+
└── broadcast/ # Queen's broadcast channel (all members read here)
|
|
15
|
+
|
|
16
|
+
Coordination board integration: every sent/received message is logged as a
|
|
17
|
+
note on the sending agent's AgentFile so the board reflects comms activity.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import logging
|
|
24
|
+
import time
|
|
25
|
+
import uuid
|
|
26
|
+
from datetime import datetime, timezone
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import List, Optional
|
|
29
|
+
|
|
30
|
+
from pydantic import BaseModel, Field
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# Sentinel value: the queen/manager role name used to discover the broadcast sender
|
|
35
|
+
_QUEEN_ROLES = frozenset({"manager", "queen"})
|
|
36
|
+
|
|
37
|
+
# File suffix used by the SKComm file transport
|
|
38
|
+
_ENVELOPE_SUFFIX = ".skc.json"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Channel configuration model
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TeamChannel(BaseModel):
|
|
47
|
+
"""Configuration for a team's comms channel.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
team_slug: Filesystem-safe identifier for the team.
|
|
51
|
+
comms_root: Root directory for all team comms.
|
|
52
|
+
members: List of agent names with registered inboxes.
|
|
53
|
+
queen: Optional manager/queen agent name with broadcast rights.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
team_slug: str
|
|
57
|
+
comms_root: Path
|
|
58
|
+
members: List[str] = Field(default_factory=list)
|
|
59
|
+
queen: Optional[str] = None
|
|
60
|
+
|
|
61
|
+
class Config:
|
|
62
|
+
arbitrary_types_allowed = True
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def team_dir(self) -> Path:
|
|
66
|
+
"""Root directory for this specific team's comms."""
|
|
67
|
+
return self.comms_root / self.team_slug
|
|
68
|
+
|
|
69
|
+
def inbox_for(self, agent_name: str) -> Path:
|
|
70
|
+
"""Return the inbox path for a given agent.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
agent_name: The agent whose inbox path to return.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Path to the agent's inbox directory.
|
|
77
|
+
"""
|
|
78
|
+
return self.team_dir / agent_name / "inbox"
|
|
79
|
+
|
|
80
|
+
def archive_for(self, agent_name: str) -> Path:
|
|
81
|
+
"""Return the archive path for a given agent.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
agent_name: The agent whose archive path to return.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Path to the agent's archive directory.
|
|
88
|
+
"""
|
|
89
|
+
return self.team_dir / agent_name / "archive"
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def broadcast_dir(self) -> Path:
|
|
93
|
+
"""Shared broadcast directory written by the queen."""
|
|
94
|
+
return self.team_dir / "broadcast"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Outgoing envelope helper
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _build_envelope(
|
|
103
|
+
sender: str,
|
|
104
|
+
recipient: str,
|
|
105
|
+
content: str,
|
|
106
|
+
thread_id: Optional[str] = None,
|
|
107
|
+
) -> dict:
|
|
108
|
+
"""Build a minimal SKComm-compatible envelope dict.
|
|
109
|
+
|
|
110
|
+
Uses the same schema as skcomm.models.MessageEnvelope so downstream
|
|
111
|
+
consumers can deserialize with MessageEnvelope.from_bytes().
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
sender: Agent name of the sender.
|
|
115
|
+
recipient: Agent name of the recipient (or "broadcast").
|
|
116
|
+
content: Plain-text message content.
|
|
117
|
+
thread_id: Optional thread identifier for grouping messages.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
dict: Envelope ready for JSON serialisation.
|
|
121
|
+
"""
|
|
122
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
123
|
+
return {
|
|
124
|
+
"skcomm_version": "1.0.0",
|
|
125
|
+
"envelope_id": str(uuid.uuid4()),
|
|
126
|
+
"sender": sender,
|
|
127
|
+
"recipient": recipient,
|
|
128
|
+
"payload": {
|
|
129
|
+
"content": content,
|
|
130
|
+
"content_type": "text",
|
|
131
|
+
"encrypted": False,
|
|
132
|
+
"compressed": False,
|
|
133
|
+
"signature": None,
|
|
134
|
+
},
|
|
135
|
+
"routing": {
|
|
136
|
+
"mode": "failover",
|
|
137
|
+
"preferred_transports": ["file"],
|
|
138
|
+
"retry_max": 3,
|
|
139
|
+
"retry_backoff": [5, 15, 60],
|
|
140
|
+
"ttl": 86400,
|
|
141
|
+
"ack_requested": False,
|
|
142
|
+
},
|
|
143
|
+
"metadata": {
|
|
144
|
+
"thread_id": thread_id,
|
|
145
|
+
"in_reply_to": None,
|
|
146
|
+
"urgency": "normal",
|
|
147
|
+
"created_at": now,
|
|
148
|
+
"expires_at": None,
|
|
149
|
+
"attempt": 0,
|
|
150
|
+
"delivered_via": "file",
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _write_envelope(inbox_dir: Path, envelope: dict) -> Path:
|
|
156
|
+
"""Atomically write an envelope to an inbox directory.
|
|
157
|
+
|
|
158
|
+
Uses a .tmp rename strategy to prevent partial reads.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
inbox_dir: Target inbox directory.
|
|
162
|
+
envelope: Envelope dict to serialise.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Path to the written envelope file.
|
|
166
|
+
"""
|
|
167
|
+
inbox_dir.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
envelope_id = envelope.get("envelope_id", str(uuid.uuid4()))
|
|
169
|
+
filename = f"{envelope_id}{_ENVELOPE_SUFFIX}"
|
|
170
|
+
target = inbox_dir / filename
|
|
171
|
+
tmp = inbox_dir / f".{filename}.tmp"
|
|
172
|
+
data = json.dumps(envelope, indent=2).encode("utf-8")
|
|
173
|
+
tmp.write_bytes(data)
|
|
174
|
+
tmp.rename(target)
|
|
175
|
+
return target
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _drain_inbox(agent_name: str, channel: TeamChannel) -> List[dict]:
|
|
179
|
+
"""Drain all pending envelopes from an agent's inbox.
|
|
180
|
+
|
|
181
|
+
Moves processed files to archive. Skips tmp files and corrupted JSON.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
agent_name: The agent whose inbox to drain.
|
|
185
|
+
channel: The TeamChannel providing directory paths.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of envelope dicts read from the inbox.
|
|
189
|
+
"""
|
|
190
|
+
inbox = channel.inbox_for(agent_name)
|
|
191
|
+
archive = channel.archive_for(agent_name)
|
|
192
|
+
|
|
193
|
+
if not inbox.exists():
|
|
194
|
+
return []
|
|
195
|
+
|
|
196
|
+
envelopes: List[dict] = []
|
|
197
|
+
for env_file in sorted(inbox.glob(f"*{_ENVELOPE_SUFFIX}")):
|
|
198
|
+
if env_file.name.startswith("."):
|
|
199
|
+
continue
|
|
200
|
+
try:
|
|
201
|
+
data = json.loads(env_file.read_bytes())
|
|
202
|
+
envelopes.append(data)
|
|
203
|
+
# Move to archive
|
|
204
|
+
archive.mkdir(parents=True, exist_ok=True)
|
|
205
|
+
dest = archive / env_file.name
|
|
206
|
+
if dest.exists():
|
|
207
|
+
dest = archive / f"{int(time.time())}-{env_file.name}"
|
|
208
|
+
env_file.rename(dest)
|
|
209
|
+
except (json.JSONDecodeError, OSError) as exc:
|
|
210
|
+
logger.warning("Skipping %s: %s", env_file, exc)
|
|
211
|
+
|
|
212
|
+
return envelopes
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ---------------------------------------------------------------------------
|
|
216
|
+
# Channel bootstrap
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def bootstrap_team_channel(
|
|
221
|
+
team_slug: str,
|
|
222
|
+
agent_names: List[str],
|
|
223
|
+
comms_root: Path,
|
|
224
|
+
queen: Optional[str] = None,
|
|
225
|
+
) -> TeamChannel:
|
|
226
|
+
"""Create the directory structure for a team comms channel.
|
|
227
|
+
|
|
228
|
+
Idempotent: safe to call multiple times for the same team.
|
|
229
|
+
Creates inbox and archive dirs for every agent plus the broadcast
|
|
230
|
+
directory if a queen is specified.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
team_slug: Filesystem-safe team identifier (matches deployment_id prefix).
|
|
234
|
+
agent_names: Ordered list of agent instance names.
|
|
235
|
+
comms_root: Root path under which team directories are created.
|
|
236
|
+
queen: Name of the managing/queen agent that gets broadcast rights.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
TeamChannel: Configured channel ready for send/receive operations.
|
|
240
|
+
"""
|
|
241
|
+
channel = TeamChannel(
|
|
242
|
+
team_slug=team_slug,
|
|
243
|
+
comms_root=comms_root,
|
|
244
|
+
members=list(agent_names),
|
|
245
|
+
queen=queen,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
for agent in agent_names:
|
|
249
|
+
channel.inbox_for(agent).mkdir(parents=True, exist_ok=True)
|
|
250
|
+
channel.archive_for(agent).mkdir(parents=True, exist_ok=True)
|
|
251
|
+
|
|
252
|
+
if queen:
|
|
253
|
+
channel.broadcast_dir.mkdir(parents=True, exist_ok=True)
|
|
254
|
+
|
|
255
|
+
logger.info(
|
|
256
|
+
"Bootstrapped comms channel for team '%s' (%d agents, queen=%s)",
|
|
257
|
+
team_slug,
|
|
258
|
+
len(agent_names),
|
|
259
|
+
queen or "none",
|
|
260
|
+
)
|
|
261
|
+
return channel
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
# Public API
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def send_to_teammate(
|
|
270
|
+
from_agent: str,
|
|
271
|
+
to_agent: str,
|
|
272
|
+
message: str,
|
|
273
|
+
channel: TeamChannel,
|
|
274
|
+
thread_id: Optional[str] = None,
|
|
275
|
+
board: Optional[object] = None,
|
|
276
|
+
) -> str:
|
|
277
|
+
"""Send a message from one agent to another within the same team.
|
|
278
|
+
|
|
279
|
+
Writes a SKComm-compatible envelope JSON file to the recipient's inbox
|
|
280
|
+
directory. Optionally logs the activity to the coordination board.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
from_agent: Sender agent name.
|
|
284
|
+
to_agent: Recipient agent name.
|
|
285
|
+
message: Plain-text message content.
|
|
286
|
+
channel: The TeamChannel for this team.
|
|
287
|
+
thread_id: Optional thread identifier for grouping related messages.
|
|
288
|
+
board: Optional Board instance for activity logging.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
str: The envelope_id of the sent message.
|
|
292
|
+
|
|
293
|
+
Raises:
|
|
294
|
+
ValueError: If to_agent is not a member of the channel.
|
|
295
|
+
"""
|
|
296
|
+
if to_agent not in channel.members:
|
|
297
|
+
raise ValueError(
|
|
298
|
+
f"Agent '{to_agent}' is not a member of team '{channel.team_slug}'. "
|
|
299
|
+
f"Known members: {channel.members}"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
envelope = _build_envelope(from_agent, to_agent, message, thread_id)
|
|
303
|
+
inbox = channel.inbox_for(to_agent)
|
|
304
|
+
path = _write_envelope(inbox, envelope)
|
|
305
|
+
|
|
306
|
+
logger.info(
|
|
307
|
+
"Agent '%s' -> '%s': %s (envelope %s)",
|
|
308
|
+
from_agent,
|
|
309
|
+
to_agent,
|
|
310
|
+
message[:60],
|
|
311
|
+
envelope["envelope_id"][:8],
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
if board is not None:
|
|
315
|
+
_log_to_board(board, from_agent, f"msg→{to_agent}: {message[:80]}")
|
|
316
|
+
|
|
317
|
+
return envelope["envelope_id"]
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def broadcast_to_team(
|
|
321
|
+
from_agent: str,
|
|
322
|
+
message: str,
|
|
323
|
+
channel: TeamChannel,
|
|
324
|
+
thread_id: Optional[str] = None,
|
|
325
|
+
board: Optional[object] = None,
|
|
326
|
+
) -> List[str]:
|
|
327
|
+
"""Broadcast a message to all team members from the queen/manager.
|
|
328
|
+
|
|
329
|
+
Writes the same envelope to every member's inbox AND to the shared
|
|
330
|
+
broadcast directory for auditability.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
from_agent: The broadcasting agent (typically the queen).
|
|
334
|
+
message: Plain-text message content.
|
|
335
|
+
channel: The TeamChannel for this team.
|
|
336
|
+
thread_id: Optional thread identifier.
|
|
337
|
+
board: Optional Board instance for activity logging.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
List[str]: envelope_ids for each delivered message.
|
|
341
|
+
|
|
342
|
+
Raises:
|
|
343
|
+
PermissionError: If from_agent is not the channel's queen.
|
|
344
|
+
"""
|
|
345
|
+
if channel.queen and from_agent != channel.queen:
|
|
346
|
+
raise PermissionError(
|
|
347
|
+
f"Only the queen agent ('{channel.queen}') may broadcast. "
|
|
348
|
+
f"Got: '{from_agent}'"
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# Write to broadcast audit log
|
|
352
|
+
broadcast_envelope = _build_envelope(from_agent, "broadcast", message, thread_id)
|
|
353
|
+
_write_envelope(channel.broadcast_dir, broadcast_envelope)
|
|
354
|
+
|
|
355
|
+
envelope_ids: List[str] = []
|
|
356
|
+
for member in channel.members:
|
|
357
|
+
if member == from_agent:
|
|
358
|
+
continue
|
|
359
|
+
env = _build_envelope(from_agent, member, message, thread_id)
|
|
360
|
+
inbox = channel.inbox_for(member)
|
|
361
|
+
_write_envelope(inbox, env)
|
|
362
|
+
envelope_ids.append(env["envelope_id"])
|
|
363
|
+
|
|
364
|
+
logger.info(
|
|
365
|
+
"Queen '%s' broadcast to %d members: %s",
|
|
366
|
+
from_agent,
|
|
367
|
+
len(envelope_ids),
|
|
368
|
+
message[:60],
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
if board is not None:
|
|
372
|
+
_log_to_board(
|
|
373
|
+
board, from_agent, f"broadcast to {len(envelope_ids)} members: {message[:80]}"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
return envelope_ids
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def receive_messages(
|
|
380
|
+
agent_name: str,
|
|
381
|
+
channel: TeamChannel,
|
|
382
|
+
board: Optional[object] = None,
|
|
383
|
+
) -> List[dict]:
|
|
384
|
+
"""Receive all pending messages for an agent.
|
|
385
|
+
|
|
386
|
+
Drains the agent's inbox and returns envelopes as dicts.
|
|
387
|
+
Processed files are moved to archive. Optionally logs activity to board.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
agent_name: The agent polling for messages.
|
|
391
|
+
channel: The TeamChannel providing inbox paths.
|
|
392
|
+
board: Optional Board instance for activity logging.
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
List[dict]: Raw envelope dicts (skcomm MessageEnvelope schema).
|
|
396
|
+
"""
|
|
397
|
+
envelopes = _drain_inbox(agent_name, channel)
|
|
398
|
+
|
|
399
|
+
if envelopes:
|
|
400
|
+
logger.debug(
|
|
401
|
+
"Agent '%s' received %d message(s)", agent_name, len(envelopes)
|
|
402
|
+
)
|
|
403
|
+
if board is not None:
|
|
404
|
+
senders = {e.get("sender", "unknown") for e in envelopes}
|
|
405
|
+
_log_to_board(
|
|
406
|
+
board,
|
|
407
|
+
agent_name,
|
|
408
|
+
f"received {len(envelopes)} msg(s) from {', '.join(sorted(senders))}",
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
return envelopes
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def receive_broadcast(
|
|
415
|
+
agent_name: str,
|
|
416
|
+
channel: TeamChannel,
|
|
417
|
+
) -> List[dict]:
|
|
418
|
+
"""Read broadcast messages from the team's broadcast directory.
|
|
419
|
+
|
|
420
|
+
Reads but does NOT archive broadcast messages (they are shared/read-only
|
|
421
|
+
from a multi-consumer perspective). Callers must track what they've read.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
agent_name: The reading agent (used for filtering self-sent messages).
|
|
425
|
+
channel: The TeamChannel providing the broadcast directory path.
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
List[dict]: Broadcast envelope dicts not sent by this agent.
|
|
429
|
+
"""
|
|
430
|
+
broadcast_dir = channel.broadcast_dir
|
|
431
|
+
if not broadcast_dir.exists():
|
|
432
|
+
return []
|
|
433
|
+
|
|
434
|
+
envelopes: List[dict] = []
|
|
435
|
+
for env_file in sorted(broadcast_dir.glob(f"*{_ENVELOPE_SUFFIX}")):
|
|
436
|
+
if env_file.name.startswith("."):
|
|
437
|
+
continue
|
|
438
|
+
try:
|
|
439
|
+
data = json.loads(env_file.read_bytes())
|
|
440
|
+
if data.get("sender") != agent_name:
|
|
441
|
+
envelopes.append(data)
|
|
442
|
+
except (json.JSONDecodeError, OSError) as exc:
|
|
443
|
+
logger.warning("Skipping broadcast file %s: %s", env_file, exc)
|
|
444
|
+
|
|
445
|
+
return envelopes
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
# ---------------------------------------------------------------------------
|
|
449
|
+
# Board integration helper
|
|
450
|
+
# ---------------------------------------------------------------------------
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _log_to_board(board: object, agent_name: str, note: str) -> None:
|
|
454
|
+
"""Append a comms activity note to an agent's coordination board file.
|
|
455
|
+
|
|
456
|
+
Silently no-ops if the board doesn't support the required interface,
|
|
457
|
+
so team_comms never hard-depends on skcapstone.coordination at import time.
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
board: A Board instance (duck-typed to avoid circular imports).
|
|
461
|
+
agent_name: Agent whose file should be updated.
|
|
462
|
+
note: Short activity description to append.
|
|
463
|
+
"""
|
|
464
|
+
try:
|
|
465
|
+
from .coordination import AgentFile
|
|
466
|
+
|
|
467
|
+
agent_file = board.load_agent(agent_name) or AgentFile(agent=agent_name) # type: ignore[attr-defined]
|
|
468
|
+
timestamp = datetime.now(timezone.utc).strftime("%H:%M:%S")
|
|
469
|
+
existing = agent_file.notes or ""
|
|
470
|
+
# Reason: keep notes bounded — prepend newest, cap at 1200 chars
|
|
471
|
+
new_entry = f"[{timestamp}] {note}"
|
|
472
|
+
combined = f"{new_entry}\n{existing}" if existing else new_entry
|
|
473
|
+
agent_file.notes = combined[:1200]
|
|
474
|
+
board.save_agent(agent_file) # type: ignore[attr-defined]
|
|
475
|
+
except Exception as exc:
|
|
476
|
+
logger.debug("Board log skipped (%s): %s", agent_name, exc)
|