@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,260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Two-factor memory verification — truth-check gate for short-term → mid-term promotion.
|
|
3
|
+
|
|
4
|
+
Before any SHORT_TERM → MID_TERM promotion this module:
|
|
5
|
+
1. Calls skseed.skill.truth_check() on the candidate memory content.
|
|
6
|
+
2. If a contradiction is found (coherence_score < 0.7 or collision_fragments non-empty):
|
|
7
|
+
a. Tags the candidate memory with tag=conflicting (saves it back to short-term).
|
|
8
|
+
b. Stores a conflict-report memory (also tag=conflicting) documenting the issue.
|
|
9
|
+
c. Fires a desktop notification with urgency=critical.
|
|
10
|
+
d. Returns VerificationResult(should_promote=False).
|
|
11
|
+
3. If truth-aligned, returns VerificationResult(should_promote=True).
|
|
12
|
+
|
|
13
|
+
Promotion is skipped until the conflict is resolved — i.e., the ``conflicting`` tag
|
|
14
|
+
is manually cleared (or via skseed_audit resolution).
|
|
15
|
+
|
|
16
|
+
Fail-open design: if skseed is not installed, or truth_check raises unexpectedly,
|
|
17
|
+
promotion proceeds normally so minimal deployments are unaffected.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import logging
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Optional
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger("skcapstone.memory_verifier")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Result type
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class VerificationResult:
|
|
37
|
+
"""Result of a pre-promotion truth-check gate.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
should_promote: Whether the memory may advance to mid-term.
|
|
41
|
+
is_conflicting: True when a contradiction was detected.
|
|
42
|
+
coherence_score: Collider coherence score (0.0–1.0).
|
|
43
|
+
collision_fragments: Contradiction strings found by the collider.
|
|
44
|
+
truth_grade: TruthGrade value string from the collider.
|
|
45
|
+
conflict_report_id: memory_id of the stored conflict-report entry,
|
|
46
|
+
present only when is_conflicting is True.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
should_promote: bool
|
|
50
|
+
is_conflicting: bool = False
|
|
51
|
+
coherence_score: float = 0.0
|
|
52
|
+
collision_fragments: list[str] = field(default_factory=list)
|
|
53
|
+
truth_grade: str = "ungraded"
|
|
54
|
+
conflict_report_id: Optional[str] = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
# Public gate
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def verify_before_promotion(home: Path, entry) -> VerificationResult:
|
|
63
|
+
"""Truth-check gate called before promoting a SHORT_TERM memory to MID_TERM.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
home: Agent home directory (~/.skcapstone).
|
|
67
|
+
entry: MemoryEntry candidate (expected to be in SHORT_TERM layer).
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
VerificationResult — consult .should_promote before proceeding.
|
|
71
|
+
"""
|
|
72
|
+
# Only gate SHORT_TERM → MID_TERM transitions
|
|
73
|
+
from .models import MemoryLayer
|
|
74
|
+
|
|
75
|
+
if entry.layer != MemoryLayer.SHORT_TERM:
|
|
76
|
+
return VerificationResult(should_promote=True)
|
|
77
|
+
|
|
78
|
+
# Skip the gate for verifier-generated meta-records (conflict reports).
|
|
79
|
+
# These entries describe the conflict itself and must not be re-checked,
|
|
80
|
+
# which would create an infinite verification cascade.
|
|
81
|
+
if getattr(entry, "source", "") == "memory_verifier":
|
|
82
|
+
return VerificationResult(should_promote=True)
|
|
83
|
+
|
|
84
|
+
# Fail-open if skseed is not installed
|
|
85
|
+
try:
|
|
86
|
+
from skseed.skill import truth_check
|
|
87
|
+
except ImportError:
|
|
88
|
+
logger.debug(
|
|
89
|
+
"skseed not installed — skipping truth-check gate (fail-open) for %s",
|
|
90
|
+
entry.memory_id,
|
|
91
|
+
)
|
|
92
|
+
return VerificationResult(should_promote=True)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
result = truth_check(
|
|
96
|
+
belief=entry.content,
|
|
97
|
+
source="model",
|
|
98
|
+
domain="memory-promotion",
|
|
99
|
+
)
|
|
100
|
+
except Exception as exc:
|
|
101
|
+
logger.warning(
|
|
102
|
+
"truth_check raised for memory %s (fail-open): %s",
|
|
103
|
+
entry.memory_id,
|
|
104
|
+
exc,
|
|
105
|
+
)
|
|
106
|
+
return VerificationResult(should_promote=True)
|
|
107
|
+
|
|
108
|
+
is_aligned: bool = result.get("is_aligned", True)
|
|
109
|
+
collider: dict = result.get("collider_result", {})
|
|
110
|
+
fragments: list[str] = collider.get("collision_fragments", [])
|
|
111
|
+
coherence: float = float(collider.get("coherence_score", 1.0))
|
|
112
|
+
grade: str = str(collider.get("truth_grade", "ungraded"))
|
|
113
|
+
|
|
114
|
+
contradiction_found = (not is_aligned) or bool(fragments)
|
|
115
|
+
|
|
116
|
+
if not contradiction_found:
|
|
117
|
+
logger.debug(
|
|
118
|
+
"Memory %s passed truth-check (coherence=%.2f grade=%s) — promotion allowed",
|
|
119
|
+
entry.memory_id,
|
|
120
|
+
coherence,
|
|
121
|
+
grade,
|
|
122
|
+
)
|
|
123
|
+
return VerificationResult(
|
|
124
|
+
should_promote=True,
|
|
125
|
+
coherence_score=coherence,
|
|
126
|
+
truth_grade=grade,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# --- Contradiction detected ---
|
|
130
|
+
logger.warning(
|
|
131
|
+
"Memory %s failed truth-check: is_aligned=%s coherence=%.2f grade=%s fragments=%d",
|
|
132
|
+
entry.memory_id,
|
|
133
|
+
is_aligned,
|
|
134
|
+
coherence,
|
|
135
|
+
grade,
|
|
136
|
+
len(fragments),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
_tag_conflicting(home, entry)
|
|
140
|
+
report_id = _store_conflict_report(home, entry, fragments, coherence, grade)
|
|
141
|
+
_fire_conflict_notification(entry, fragments, coherence)
|
|
142
|
+
|
|
143
|
+
return VerificationResult(
|
|
144
|
+
should_promote=False,
|
|
145
|
+
is_conflicting=True,
|
|
146
|
+
coherence_score=coherence,
|
|
147
|
+
collision_fragments=fragments,
|
|
148
|
+
truth_grade=grade,
|
|
149
|
+
conflict_report_id=report_id,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
# Helpers
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _tag_conflicting(home: Path, entry) -> None:
|
|
159
|
+
"""Add tag=conflicting to the candidate entry and re-save it to short-term."""
|
|
160
|
+
if "conflicting" not in entry.tags:
|
|
161
|
+
entry.tags = list(entry.tags) + ["conflicting"]
|
|
162
|
+
try:
|
|
163
|
+
from .memory_engine import _save_entry, _update_index
|
|
164
|
+
|
|
165
|
+
_save_entry(home, entry)
|
|
166
|
+
_update_index(home, entry)
|
|
167
|
+
logger.info(
|
|
168
|
+
"Tagged memory %s as conflicting (stays in short-term)",
|
|
169
|
+
entry.memory_id,
|
|
170
|
+
)
|
|
171
|
+
except Exception as exc:
|
|
172
|
+
logger.error(
|
|
173
|
+
"Failed to persist conflicting tag for %s: %s",
|
|
174
|
+
entry.memory_id,
|
|
175
|
+
exc,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _store_conflict_report(
|
|
180
|
+
home: Path,
|
|
181
|
+
entry,
|
|
182
|
+
fragments: list[str],
|
|
183
|
+
coherence: float,
|
|
184
|
+
grade: str,
|
|
185
|
+
) -> Optional[str]:
|
|
186
|
+
"""Store a short-term conflict-report memory documenting the failed truth-check.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
memory_id of the created report, or None on failure.
|
|
190
|
+
"""
|
|
191
|
+
if fragments:
|
|
192
|
+
fragment_summary = "; ".join(fragments[:3])
|
|
193
|
+
if len(fragments) > 3:
|
|
194
|
+
fragment_summary += f" … (+{len(fragments) - 3} more)"
|
|
195
|
+
else:
|
|
196
|
+
fragment_summary = "coherence below threshold"
|
|
197
|
+
|
|
198
|
+
preview = entry.content[:120]
|
|
199
|
+
if len(entry.content) > 120:
|
|
200
|
+
preview += "…"
|
|
201
|
+
|
|
202
|
+
report_content = (
|
|
203
|
+
f"[CONFLICT REPORT] Memory {entry.memory_id!r} failed truth-check — "
|
|
204
|
+
f"SHORT_TERM→MID_TERM promotion BLOCKED. "
|
|
205
|
+
f"Coherence: {coherence:.2f}, Grade: {grade}. "
|
|
206
|
+
f"Contradictions: {fragment_summary}. "
|
|
207
|
+
f"Content preview: {preview!r}"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
from .memory_engine import store as mem_store
|
|
212
|
+
from .models import MemoryLayer
|
|
213
|
+
|
|
214
|
+
report = mem_store(
|
|
215
|
+
home=home,
|
|
216
|
+
content=report_content,
|
|
217
|
+
tags=["conflicting", "truth-check", "promotion-blocked"],
|
|
218
|
+
source="memory_verifier",
|
|
219
|
+
importance=0.6, # below 0.7: stays in short-term until conflict resolved
|
|
220
|
+
layer=MemoryLayer.SHORT_TERM,
|
|
221
|
+
)
|
|
222
|
+
logger.info(
|
|
223
|
+
"Stored conflict report %s for candidate %s",
|
|
224
|
+
report.memory_id,
|
|
225
|
+
entry.memory_id,
|
|
226
|
+
)
|
|
227
|
+
return report.memory_id
|
|
228
|
+
except Exception as exc:
|
|
229
|
+
logger.error("Failed to store conflict report for %s: %s", entry.memory_id, exc)
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _fire_conflict_notification(entry, fragments: list[str], coherence: float) -> None:
|
|
234
|
+
"""Dispatch a critical desktop notification for a promotion conflict."""
|
|
235
|
+
preview = entry.content[:60]
|
|
236
|
+
if len(entry.content) > 60:
|
|
237
|
+
preview += "…"
|
|
238
|
+
|
|
239
|
+
if fragments:
|
|
240
|
+
hint = f" — {fragments[0][:50]}"
|
|
241
|
+
else:
|
|
242
|
+
hint = f" (coherence {coherence:.2f})"
|
|
243
|
+
|
|
244
|
+
title = "Memory Conflict Detected"
|
|
245
|
+
body = f"Promotion blocked [{entry.memory_id}]: {preview}{hint}"
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
from .notifications import notify
|
|
249
|
+
|
|
250
|
+
notify(title=title, body=body, urgency="critical")
|
|
251
|
+
logger.info(
|
|
252
|
+
"Fired critical notification for conflicting memory %s",
|
|
253
|
+
entry.memory_id,
|
|
254
|
+
)
|
|
255
|
+
except Exception as exc:
|
|
256
|
+
logger.error(
|
|
257
|
+
"Failed to fire conflict notification for %s: %s",
|
|
258
|
+
entry.memory_id,
|
|
259
|
+
exc,
|
|
260
|
+
)
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SKChat message encryption — AES-256-GCM content encryption.
|
|
3
|
+
|
|
4
|
+
Provides encrypt/decrypt for chat message content with a key derived
|
|
5
|
+
from the agent's KMS service key (label: 'skchat').
|
|
6
|
+
|
|
7
|
+
Wire format (JSON envelope):
|
|
8
|
+
{
|
|
9
|
+
"skchat_encrypted": true,
|
|
10
|
+
"v": 1,
|
|
11
|
+
"ciphertext": "<base64(nonce_12 || ciphertext+tag_16)>"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Key derivation:
|
|
15
|
+
KeyStore.derive_service_key("skchat") → 32-byte AES key
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
from skcapstone.message_crypto import encrypt_content, decrypt_content
|
|
19
|
+
|
|
20
|
+
token = encrypt_content("hello", home) # → JSON envelope str
|
|
21
|
+
plain = decrypt_content(token, home) # → "hello"
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import base64
|
|
27
|
+
import json
|
|
28
|
+
import logging
|
|
29
|
+
import os
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Optional
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger("skcapstone.message_crypto")
|
|
34
|
+
|
|
35
|
+
# Marker key in the envelope JSON
|
|
36
|
+
_MARKER = "skchat_encrypted"
|
|
37
|
+
_VERSION = 1
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Low-level AES-256-GCM primitives (no KMS dependency)
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
def encrypt_message(plaintext: str, key: bytes) -> str:
|
|
45
|
+
"""Encrypt a plaintext string with AES-256-GCM.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
plaintext: UTF-8 message content.
|
|
49
|
+
key: 32-byte AES key.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Base64-encoded bytes: nonce (12) || ciphertext+tag.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
ValueError: If key is not exactly 32 bytes.
|
|
56
|
+
"""
|
|
57
|
+
if len(key) != 32:
|
|
58
|
+
raise ValueError(f"AES-256 requires a 32-byte key, got {len(key)}")
|
|
59
|
+
|
|
60
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
61
|
+
|
|
62
|
+
nonce = os.urandom(12)
|
|
63
|
+
aesgcm = AESGCM(key)
|
|
64
|
+
ct = aesgcm.encrypt(nonce, plaintext.encode("utf-8"), None)
|
|
65
|
+
return base64.b64encode(nonce + ct).decode("ascii")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def decrypt_message(token: str, key: bytes) -> str:
|
|
69
|
+
"""Decrypt a base64 AES-256-GCM token.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
token: Base64-encoded nonce||ciphertext+tag (from encrypt_message).
|
|
73
|
+
key: 32-byte AES key.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Decrypted plaintext string.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
ValueError: If key is not exactly 32 bytes.
|
|
80
|
+
cryptography.exceptions.InvalidTag: If authentication fails (wrong key
|
|
81
|
+
or tampered ciphertext).
|
|
82
|
+
"""
|
|
83
|
+
if len(key) != 32:
|
|
84
|
+
raise ValueError(f"AES-256 requires a 32-byte key, got {len(key)}")
|
|
85
|
+
|
|
86
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
87
|
+
|
|
88
|
+
raw = base64.b64decode(token)
|
|
89
|
+
if len(raw) < 12:
|
|
90
|
+
raise ValueError("Ciphertext too short — must be at least 12 bytes (nonce)")
|
|
91
|
+
|
|
92
|
+
nonce, ct = raw[:12], raw[12:]
|
|
93
|
+
aesgcm = AESGCM(key)
|
|
94
|
+
return aesgcm.decrypt(nonce, ct, None).decode("utf-8")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Envelope helpers
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
def pack_encrypted(ciphertext_b64: str) -> str:
|
|
102
|
+
"""Wrap a base64 ciphertext in the skchat_encrypted JSON envelope.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
ciphertext_b64: Base64 token from encrypt_message.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
JSON string with the skchat_encrypted envelope.
|
|
109
|
+
"""
|
|
110
|
+
return json.dumps({
|
|
111
|
+
_MARKER: True,
|
|
112
|
+
"v": _VERSION,
|
|
113
|
+
"ciphertext": ciphertext_b64,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def unpack_encrypted(content: str) -> Optional[str]:
|
|
118
|
+
"""Extract the base64 ciphertext from an encrypted envelope, or None.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
content: Message content string (may or may not be an envelope).
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Base64 ciphertext if content is an encrypted envelope, else None.
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
data = json.loads(content)
|
|
128
|
+
if data.get(_MARKER) is True and "ciphertext" in data:
|
|
129
|
+
return data["ciphertext"]
|
|
130
|
+
except (json.JSONDecodeError, TypeError, AttributeError):
|
|
131
|
+
pass
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def is_encrypted_content(content: str) -> bool:
|
|
136
|
+
"""Return True if content is an skchat_encrypted envelope.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
content: Message content string.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
bool: True when the content carries an encrypted payload.
|
|
143
|
+
"""
|
|
144
|
+
return unpack_encrypted(content) is not None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# KMS-backed key derivation
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
def derive_chat_key(home: Path) -> bytes:
|
|
152
|
+
"""Derive the skchat service key from the agent's KMS.
|
|
153
|
+
|
|
154
|
+
Initializes the KeyStore (creating it if absent) and derives a
|
|
155
|
+
deterministic 32-byte AES key for the 'skchat' service using
|
|
156
|
+
HKDF-SHA256.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
home: Agent home directory (~/.skcapstone).
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
32-byte key material.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
RuntimeError: If key derivation fails.
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
from .kms import KeyStore
|
|
169
|
+
|
|
170
|
+
store = KeyStore(home)
|
|
171
|
+
store.initialize()
|
|
172
|
+
record = store.derive_service_key("skchat")
|
|
173
|
+
return store.get_key_material(record.key_id)
|
|
174
|
+
except Exception as exc:
|
|
175
|
+
raise RuntimeError(f"Failed to derive skchat key from KMS: {exc}") from exc
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# Convenience API (KMS-aware)
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
def encrypt_content(plaintext: str, home: Path) -> str:
|
|
183
|
+
"""Encrypt message content using the agent's KMS-derived skchat key.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
plaintext: Message text to encrypt.
|
|
187
|
+
home: Agent home directory.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
JSON envelope string ready to use as message content.
|
|
191
|
+
"""
|
|
192
|
+
key = derive_chat_key(home)
|
|
193
|
+
token = encrypt_message(plaintext, key)
|
|
194
|
+
return pack_encrypted(token)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def decrypt_content(content: str, home: Path) -> str:
|
|
198
|
+
"""Decrypt an encrypted message envelope using the KMS-derived skchat key.
|
|
199
|
+
|
|
200
|
+
If the content is not an encrypted envelope, returns it unchanged
|
|
201
|
+
(pass-through for plaintext messages).
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
content: Message content (envelope or plain).
|
|
205
|
+
home: Agent home directory.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Decrypted plaintext (or original content if not encrypted).
|
|
209
|
+
"""
|
|
210
|
+
token = unpack_encrypted(content)
|
|
211
|
+
if token is None:
|
|
212
|
+
return content
|
|
213
|
+
|
|
214
|
+
key = derive_chat_key(home)
|
|
215
|
+
return decrypt_message(token, key)
|