@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,339 @@
|
|
|
1
|
+
"""Tests for the Memory Adapter — bridge between skcapstone and skmemory."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from unittest.mock import MagicMock, patch
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from skcapstone.memory_adapter import (
|
|
11
|
+
_LAYER_FROM_SKMEMORY,
|
|
12
|
+
_LAYER_TO_SKMEMORY,
|
|
13
|
+
entry_to_memory,
|
|
14
|
+
get_unified,
|
|
15
|
+
memory_to_entry,
|
|
16
|
+
verify_sync,
|
|
17
|
+
reindex_all,
|
|
18
|
+
)
|
|
19
|
+
from skcapstone.models import MemoryEntry, MemoryLayer
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Layer mapping
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TestLayerMapping:
|
|
28
|
+
"""Tests for layer name conversion."""
|
|
29
|
+
|
|
30
|
+
def test_to_skmemory_mapping(self) -> None:
|
|
31
|
+
"""skcapstone layers map to skmemory strings."""
|
|
32
|
+
assert _LAYER_TO_SKMEMORY[MemoryLayer.SHORT_TERM] == "short-term"
|
|
33
|
+
assert _LAYER_TO_SKMEMORY[MemoryLayer.MID_TERM] == "mid-term"
|
|
34
|
+
assert _LAYER_TO_SKMEMORY[MemoryLayer.LONG_TERM] == "long-term"
|
|
35
|
+
|
|
36
|
+
def test_from_skmemory_mapping(self) -> None:
|
|
37
|
+
"""skmemory strings map back to skcapstone layers."""
|
|
38
|
+
assert _LAYER_FROM_SKMEMORY["short-term"] == MemoryLayer.SHORT_TERM
|
|
39
|
+
assert _LAYER_FROM_SKMEMORY["mid-term"] == MemoryLayer.MID_TERM
|
|
40
|
+
assert _LAYER_FROM_SKMEMORY["long-term"] == MemoryLayer.LONG_TERM
|
|
41
|
+
|
|
42
|
+
def test_roundtrip(self) -> None:
|
|
43
|
+
"""Layer mappings are invertible."""
|
|
44
|
+
for layer in MemoryLayer:
|
|
45
|
+
sk_str = _LAYER_TO_SKMEMORY[layer]
|
|
46
|
+
assert _LAYER_FROM_SKMEMORY[sk_str] == layer
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# entry_to_memory conversion
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TestEntryToMemory:
|
|
55
|
+
"""Tests for converting MemoryEntry to skmemory Memory."""
|
|
56
|
+
|
|
57
|
+
def test_basic_conversion(self) -> None:
|
|
58
|
+
"""Basic entry converts to Memory with correct fields."""
|
|
59
|
+
entry = MemoryEntry(
|
|
60
|
+
memory_id="abc123",
|
|
61
|
+
content="Test memory content",
|
|
62
|
+
tags=["test", "unit"],
|
|
63
|
+
source="cli",
|
|
64
|
+
layer=MemoryLayer.SHORT_TERM,
|
|
65
|
+
importance=0.7,
|
|
66
|
+
)
|
|
67
|
+
mem = entry_to_memory(entry)
|
|
68
|
+
assert mem.id == "abc123"
|
|
69
|
+
assert mem.content == "Test memory content"
|
|
70
|
+
assert mem.tags == ["test", "unit"]
|
|
71
|
+
assert mem.source == "cli"
|
|
72
|
+
assert mem.layer.value == "short-term"
|
|
73
|
+
|
|
74
|
+
def test_title_from_content(self) -> None:
|
|
75
|
+
"""Title is derived from first 80 chars of content."""
|
|
76
|
+
entry = MemoryEntry(
|
|
77
|
+
memory_id="abc",
|
|
78
|
+
content="A" * 100,
|
|
79
|
+
)
|
|
80
|
+
mem = entry_to_memory(entry)
|
|
81
|
+
assert len(mem.title) == 80
|
|
82
|
+
|
|
83
|
+
def test_emotional_intensity_from_importance(self) -> None:
|
|
84
|
+
"""Importance maps to emotional intensity (x10)."""
|
|
85
|
+
entry = MemoryEntry(
|
|
86
|
+
memory_id="abc",
|
|
87
|
+
content="Test",
|
|
88
|
+
importance=0.8,
|
|
89
|
+
)
|
|
90
|
+
mem = entry_to_memory(entry)
|
|
91
|
+
assert mem.emotional.intensity == 8.0
|
|
92
|
+
|
|
93
|
+
def test_metadata_preserved(self) -> None:
|
|
94
|
+
"""Extra metadata fields are preserved."""
|
|
95
|
+
entry = MemoryEntry(
|
|
96
|
+
memory_id="abc",
|
|
97
|
+
content="Test",
|
|
98
|
+
metadata={"custom_key": "custom_value"},
|
|
99
|
+
access_count=5,
|
|
100
|
+
soul_context="lumina",
|
|
101
|
+
)
|
|
102
|
+
mem = entry_to_memory(entry)
|
|
103
|
+
assert mem.metadata["access_count"] == 5
|
|
104
|
+
assert mem.metadata["soul_context"] == "lumina"
|
|
105
|
+
assert mem.metadata["custom_key"] == "custom_value"
|
|
106
|
+
|
|
107
|
+
def test_all_layers(self) -> None:
|
|
108
|
+
"""All layer types convert correctly."""
|
|
109
|
+
for layer in MemoryLayer:
|
|
110
|
+
entry = MemoryEntry(
|
|
111
|
+
memory_id=f"id-{layer.value}",
|
|
112
|
+
content="Test",
|
|
113
|
+
layer=layer,
|
|
114
|
+
)
|
|
115
|
+
mem = entry_to_memory(entry)
|
|
116
|
+
assert mem.layer.value == _LAYER_TO_SKMEMORY[layer]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
# memory_to_entry conversion
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class TestMemoryToEntry:
|
|
125
|
+
"""Tests for converting skmemory Memory to MemoryEntry."""
|
|
126
|
+
|
|
127
|
+
def test_roundtrip(self) -> None:
|
|
128
|
+
"""Entry → Memory → Entry preserves key fields."""
|
|
129
|
+
original = MemoryEntry(
|
|
130
|
+
memory_id="round-trip",
|
|
131
|
+
content="Round trip test",
|
|
132
|
+
tags=["test"],
|
|
133
|
+
source="mcp",
|
|
134
|
+
layer=MemoryLayer.MID_TERM,
|
|
135
|
+
importance=0.6,
|
|
136
|
+
access_count=3,
|
|
137
|
+
)
|
|
138
|
+
mem = entry_to_memory(original)
|
|
139
|
+
restored = memory_to_entry(mem)
|
|
140
|
+
assert restored.memory_id == original.memory_id
|
|
141
|
+
assert restored.content == original.content
|
|
142
|
+
assert restored.tags == original.tags
|
|
143
|
+
assert restored.source == original.source
|
|
144
|
+
assert restored.layer == original.layer
|
|
145
|
+
assert abs(restored.importance - original.importance) < 0.01
|
|
146
|
+
assert restored.access_count == original.access_count
|
|
147
|
+
|
|
148
|
+
def test_importance_clamped(self) -> None:
|
|
149
|
+
"""Importance is clamped to [0.0, 1.0]."""
|
|
150
|
+
from skmemory.models import EmotionalSnapshot, Memory, MemoryLayer as SKLayer
|
|
151
|
+
|
|
152
|
+
mem = Memory(
|
|
153
|
+
id="test",
|
|
154
|
+
title="test",
|
|
155
|
+
content="test",
|
|
156
|
+
layer=SKLayer("short-term"),
|
|
157
|
+
tags=[],
|
|
158
|
+
source="test",
|
|
159
|
+
emotional=EmotionalSnapshot(intensity=10.0), # 10 / 10 = 1.0
|
|
160
|
+
metadata={},
|
|
161
|
+
)
|
|
162
|
+
entry = memory_to_entry(mem)
|
|
163
|
+
assert entry.importance <= 1.0
|
|
164
|
+
assert entry.importance >= 0.0
|
|
165
|
+
|
|
166
|
+
def test_soul_context_extracted(self) -> None:
|
|
167
|
+
"""soul_context is extracted from metadata."""
|
|
168
|
+
original = MemoryEntry(
|
|
169
|
+
memory_id="soul-test",
|
|
170
|
+
content="Soul context test",
|
|
171
|
+
soul_context="opus",
|
|
172
|
+
)
|
|
173
|
+
mem = entry_to_memory(original)
|
|
174
|
+
restored = memory_to_entry(mem)
|
|
175
|
+
assert restored.soul_context == "opus"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# get_unified singleton
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class TestGetUnified:
|
|
184
|
+
"""Tests for the unified store singleton."""
|
|
185
|
+
|
|
186
|
+
def test_returns_none_without_skmemory(self) -> None:
|
|
187
|
+
"""Returns None when skmemory is not available."""
|
|
188
|
+
import skcapstone.memory_adapter as ma
|
|
189
|
+
|
|
190
|
+
old_checked = ma._unified_checked
|
|
191
|
+
old_store = ma._unified_store
|
|
192
|
+
try:
|
|
193
|
+
ma._unified_checked = False
|
|
194
|
+
ma._unified_store = None
|
|
195
|
+
with patch.object(ma, "_skmemory_available", return_value=False):
|
|
196
|
+
with patch.object(ma, "_get_store", return_value=None):
|
|
197
|
+
result = get_unified()
|
|
198
|
+
assert result is None
|
|
199
|
+
finally:
|
|
200
|
+
ma._unified_checked = old_checked
|
|
201
|
+
ma._unified_store = old_store
|
|
202
|
+
|
|
203
|
+
def test_caches_result(self) -> None:
|
|
204
|
+
"""Second call returns cached store, doesn't recreate."""
|
|
205
|
+
import skcapstone.memory_adapter as ma
|
|
206
|
+
|
|
207
|
+
old_checked = ma._unified_checked
|
|
208
|
+
old_store = ma._unified_store
|
|
209
|
+
try:
|
|
210
|
+
ma._unified_checked = False
|
|
211
|
+
ma._unified_store = None
|
|
212
|
+
mock_store = MagicMock()
|
|
213
|
+
with patch.object(ma, "_get_store", return_value=mock_store) as mock_get:
|
|
214
|
+
first = get_unified()
|
|
215
|
+
second = get_unified()
|
|
216
|
+
assert first is second
|
|
217
|
+
assert mock_get.call_count == 1
|
|
218
|
+
finally:
|
|
219
|
+
ma._unified_checked = old_checked
|
|
220
|
+
ma._unified_store = old_store
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# ---------------------------------------------------------------------------
|
|
224
|
+
# verify_sync
|
|
225
|
+
# ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class TestVerifySync:
|
|
229
|
+
"""Tests for sync verification."""
|
|
230
|
+
|
|
231
|
+
def test_returns_not_available_without_skmemory(self) -> None:
|
|
232
|
+
"""Returns error dict when no store available."""
|
|
233
|
+
import skcapstone.memory_adapter as ma
|
|
234
|
+
|
|
235
|
+
old_checked = ma._unified_checked
|
|
236
|
+
old_store = ma._unified_store
|
|
237
|
+
try:
|
|
238
|
+
ma._unified_checked = True
|
|
239
|
+
ma._unified_store = None
|
|
240
|
+
result = verify_sync()
|
|
241
|
+
assert result["synced"] is False
|
|
242
|
+
assert "not available" in result["reason"]
|
|
243
|
+
finally:
|
|
244
|
+
ma._unified_checked = old_checked
|
|
245
|
+
ma._unified_store = old_store
|
|
246
|
+
|
|
247
|
+
def test_detects_sync(self) -> None:
|
|
248
|
+
"""Reports synced when counts match."""
|
|
249
|
+
mock_store = MagicMock()
|
|
250
|
+
mock_store.health.return_value = {
|
|
251
|
+
"primary": {"ok": True},
|
|
252
|
+
"vector": {"ok": True, "point_count": 10},
|
|
253
|
+
}
|
|
254
|
+
mock_store.primary.stats.return_value = {"total": 10}
|
|
255
|
+
|
|
256
|
+
import skcapstone.memory_adapter as ma
|
|
257
|
+
|
|
258
|
+
old_checked = ma._unified_checked
|
|
259
|
+
old_store = ma._unified_store
|
|
260
|
+
try:
|
|
261
|
+
ma._unified_checked = True
|
|
262
|
+
ma._unified_store = mock_store
|
|
263
|
+
result = verify_sync()
|
|
264
|
+
assert result["synced"] is True
|
|
265
|
+
finally:
|
|
266
|
+
ma._unified_checked = old_checked
|
|
267
|
+
ma._unified_store = old_store
|
|
268
|
+
|
|
269
|
+
def test_detects_mismatch(self) -> None:
|
|
270
|
+
"""Reports not synced when counts differ."""
|
|
271
|
+
mock_store = MagicMock()
|
|
272
|
+
mock_store.health.return_value = {
|
|
273
|
+
"primary": {"ok": True},
|
|
274
|
+
"vector": {"ok": True, "point_count": 5},
|
|
275
|
+
}
|
|
276
|
+
mock_store.primary.stats.return_value = {"total": 10}
|
|
277
|
+
|
|
278
|
+
import skcapstone.memory_adapter as ma
|
|
279
|
+
|
|
280
|
+
old_checked = ma._unified_checked
|
|
281
|
+
old_store = ma._unified_store
|
|
282
|
+
try:
|
|
283
|
+
ma._unified_checked = True
|
|
284
|
+
ma._unified_store = mock_store
|
|
285
|
+
result = verify_sync()
|
|
286
|
+
assert result["synced"] is False
|
|
287
|
+
assert "mismatch" in result.get("reason", "").lower()
|
|
288
|
+
finally:
|
|
289
|
+
ma._unified_checked = old_checked
|
|
290
|
+
ma._unified_store = old_store
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# ---------------------------------------------------------------------------
|
|
294
|
+
# reindex_all
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class TestReindexAll:
|
|
299
|
+
"""Tests for full reindex."""
|
|
300
|
+
|
|
301
|
+
def test_returns_not_available_without_store(self) -> None:
|
|
302
|
+
"""Returns error when no store available."""
|
|
303
|
+
import skcapstone.memory_adapter as ma
|
|
304
|
+
|
|
305
|
+
old_checked = ma._unified_checked
|
|
306
|
+
old_store = ma._unified_store
|
|
307
|
+
try:
|
|
308
|
+
ma._unified_checked = True
|
|
309
|
+
ma._unified_store = None
|
|
310
|
+
result = reindex_all()
|
|
311
|
+
assert result["ok"] is False
|
|
312
|
+
finally:
|
|
313
|
+
ma._unified_checked = old_checked
|
|
314
|
+
ma._unified_store = old_store
|
|
315
|
+
|
|
316
|
+
def test_reindexes_all_memories(self) -> None:
|
|
317
|
+
"""Reindex processes all memories from primary."""
|
|
318
|
+
mock_mem = MagicMock()
|
|
319
|
+
mock_mem.id = "test-1"
|
|
320
|
+
mock_store = MagicMock()
|
|
321
|
+
mock_store.list_memories.return_value = [mock_mem, mock_mem]
|
|
322
|
+
mock_store.vector = MagicMock()
|
|
323
|
+
mock_store.graph = MagicMock()
|
|
324
|
+
|
|
325
|
+
import skcapstone.memory_adapter as ma
|
|
326
|
+
|
|
327
|
+
old_checked = ma._unified_checked
|
|
328
|
+
old_store = ma._unified_store
|
|
329
|
+
try:
|
|
330
|
+
ma._unified_checked = True
|
|
331
|
+
ma._unified_store = mock_store
|
|
332
|
+
result = reindex_all()
|
|
333
|
+
assert result["ok"] is True
|
|
334
|
+
assert result["total"] == 2
|
|
335
|
+
assert result["vector_indexed"] == 2
|
|
336
|
+
assert result["graph_indexed"] == 2
|
|
337
|
+
finally:
|
|
338
|
+
ma._unified_checked = old_checked
|
|
339
|
+
ma._unified_store = old_store
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Tests for the memory curator module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from skcapstone.memory_engine import list_memories, recall, store
|
|
10
|
+
from skcapstone.memory_curator import (
|
|
11
|
+
CurationResult,
|
|
12
|
+
MemoryCurator,
|
|
13
|
+
_content_hash,
|
|
14
|
+
_suggest_tags,
|
|
15
|
+
)
|
|
16
|
+
from skcapstone.models import MemoryLayer
|
|
17
|
+
from skcapstone.pillars.memory import initialize_memory
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture
|
|
21
|
+
def curator_home(tmp_agent_home: Path) -> Path:
|
|
22
|
+
"""Provide an agent home with memory initialized."""
|
|
23
|
+
initialize_memory(tmp_agent_home)
|
|
24
|
+
return tmp_agent_home
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TestSuggestTags:
|
|
28
|
+
"""Tests for the auto-tag suggestion function."""
|
|
29
|
+
|
|
30
|
+
def test_detects_capauth(self):
|
|
31
|
+
"""Finds capauth mentions."""
|
|
32
|
+
tags = _suggest_tags("The capauth system handles identity", [])
|
|
33
|
+
assert "capauth" in tags
|
|
34
|
+
|
|
35
|
+
def test_skips_existing_tags(self):
|
|
36
|
+
"""Doesn't suggest tags already present."""
|
|
37
|
+
tags = _suggest_tags("The capauth system", ["capauth"])
|
|
38
|
+
assert "capauth" not in tags
|
|
39
|
+
|
|
40
|
+
def test_multiple_suggestions(self):
|
|
41
|
+
"""Detects multiple patterns in one text."""
|
|
42
|
+
tags = _suggest_tags("skcapstone uses MCP and PGP for security", [])
|
|
43
|
+
assert "skcapstone" in tags
|
|
44
|
+
assert "mcp" in tags
|
|
45
|
+
assert "pgp" in tags
|
|
46
|
+
|
|
47
|
+
def test_no_suggestions_for_generic(self):
|
|
48
|
+
"""Generic text gets no tag suggestions."""
|
|
49
|
+
tags = _suggest_tags("The weather is nice today", [])
|
|
50
|
+
assert tags == []
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TestContentHash:
|
|
54
|
+
"""Tests for the deduplication hash function."""
|
|
55
|
+
|
|
56
|
+
def test_identical_content(self):
|
|
57
|
+
"""Same content produces same hash."""
|
|
58
|
+
assert _content_hash("hello world") == _content_hash("hello world")
|
|
59
|
+
|
|
60
|
+
def test_case_insensitive(self):
|
|
61
|
+
"""Hash is case-insensitive."""
|
|
62
|
+
assert _content_hash("Hello World") == _content_hash("hello world")
|
|
63
|
+
|
|
64
|
+
def test_whitespace_normalized(self):
|
|
65
|
+
"""Extra whitespace is normalized before hashing."""
|
|
66
|
+
assert _content_hash("hello world") == _content_hash("hello world")
|
|
67
|
+
|
|
68
|
+
def test_different_content(self):
|
|
69
|
+
"""Different content produces different hashes."""
|
|
70
|
+
assert _content_hash("alpha") != _content_hash("beta")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TestCuratorAutoTag:
|
|
74
|
+
"""Tests for the auto-tagging pass."""
|
|
75
|
+
|
|
76
|
+
def test_adds_missing_tags(self, curator_home: Path):
|
|
77
|
+
"""Auto-tag adds relevant tags to untagged memories."""
|
|
78
|
+
store(curator_home, "The skcapstone MCP server exposes tools", tags=[])
|
|
79
|
+
curator = MemoryCurator(curator_home)
|
|
80
|
+
result = curator.curate(promote=False, dedupe=False)
|
|
81
|
+
|
|
82
|
+
assert len(result.tagged) >= 1
|
|
83
|
+
memories = list_memories(curator_home)
|
|
84
|
+
tagged_mem = next((m for m in memories if "skcapstone" in m.tags), None)
|
|
85
|
+
assert tagged_mem is not None
|
|
86
|
+
|
|
87
|
+
def test_dry_run_no_changes(self, curator_home: Path):
|
|
88
|
+
"""Dry run reports changes without applying them."""
|
|
89
|
+
store(curator_home, "The capauth PGP system", tags=[])
|
|
90
|
+
curator = MemoryCurator(curator_home)
|
|
91
|
+
result = curator.curate(dry_run=True, promote=False, dedupe=False)
|
|
92
|
+
|
|
93
|
+
assert len(result.tagged) >= 1
|
|
94
|
+
memories = list_memories(curator_home)
|
|
95
|
+
for m in memories:
|
|
96
|
+
assert "capauth" not in m.tags
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TestCuratorPromote:
|
|
100
|
+
"""Tests for the promotion pass."""
|
|
101
|
+
|
|
102
|
+
def test_promotes_high_access(self, curator_home: Path):
|
|
103
|
+
"""Memories with high access count get promoted."""
|
|
104
|
+
entry = store(curator_home, "Frequently accessed memory", importance=0.3)
|
|
105
|
+
entry.access_count = 5
|
|
106
|
+
from skcapstone.memory_engine import _save_entry
|
|
107
|
+
_save_entry(curator_home, entry)
|
|
108
|
+
|
|
109
|
+
curator = MemoryCurator(curator_home)
|
|
110
|
+
result = curator.curate(auto_tag=False, dedupe=False)
|
|
111
|
+
|
|
112
|
+
assert len(result.promoted) >= 1
|
|
113
|
+
|
|
114
|
+
def test_promotes_high_importance(self, curator_home: Path):
|
|
115
|
+
"""High importance short-term memories get promoted."""
|
|
116
|
+
store(curator_home, "Very important memory", importance=0.8)
|
|
117
|
+
|
|
118
|
+
curator = MemoryCurator(curator_home)
|
|
119
|
+
result = curator.curate(auto_tag=False, dedupe=False)
|
|
120
|
+
|
|
121
|
+
# Reason: importance >= 0.7 auto-promotes to mid-term at store time,
|
|
122
|
+
# so this won't be in short-term. Store at 0.65 instead.
|
|
123
|
+
store(curator_home, "Almost important", importance=0.65)
|
|
124
|
+
entry = list_memories(curator_home, limit=1)[0]
|
|
125
|
+
entry.access_count = 4
|
|
126
|
+
_save_entry = __import__("skcapstone.memory_engine", fromlist=["_save_entry"])._save_entry
|
|
127
|
+
_save_entry(curator_home, entry)
|
|
128
|
+
|
|
129
|
+
result2 = curator.curate(auto_tag=False, dedupe=False)
|
|
130
|
+
# At least the high-access one should promote
|
|
131
|
+
assert result2.total_scanned >= 1
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class TestCuratorDedupe:
|
|
135
|
+
"""Tests for the deduplication pass."""
|
|
136
|
+
|
|
137
|
+
def test_removes_exact_duplicates(self, curator_home: Path):
|
|
138
|
+
"""Identical content is deduplicated."""
|
|
139
|
+
store(curator_home, "Duplicate memory content here", importance=0.4)
|
|
140
|
+
store(curator_home, "Duplicate memory content here", importance=0.3)
|
|
141
|
+
|
|
142
|
+
curator = MemoryCurator(curator_home)
|
|
143
|
+
result = curator.curate(auto_tag=False, promote=False)
|
|
144
|
+
|
|
145
|
+
assert len(result.deduped) >= 1
|
|
146
|
+
|
|
147
|
+
def test_keeps_higher_tier(self, curator_home: Path):
|
|
148
|
+
"""When deduping, the higher-tier memory is kept."""
|
|
149
|
+
store(curator_home, "Keep this important memory", importance=0.9)
|
|
150
|
+
store(curator_home, "Keep this important memory", importance=0.3)
|
|
151
|
+
|
|
152
|
+
before = len(list_memories(curator_home))
|
|
153
|
+
curator = MemoryCurator(curator_home)
|
|
154
|
+
curator.curate(auto_tag=False, promote=False)
|
|
155
|
+
after = len(list_memories(curator_home))
|
|
156
|
+
|
|
157
|
+
assert after < before
|
|
158
|
+
|
|
159
|
+
def test_no_false_positives(self, curator_home: Path):
|
|
160
|
+
"""Different content is not deduplicated."""
|
|
161
|
+
store(curator_home, "First unique memory about cats")
|
|
162
|
+
store(curator_home, "Second unique memory about dogs")
|
|
163
|
+
|
|
164
|
+
curator = MemoryCurator(curator_home)
|
|
165
|
+
result = curator.curate(auto_tag=False, promote=False)
|
|
166
|
+
|
|
167
|
+
assert len(result.deduped) == 0
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class TestCuratorStats:
|
|
171
|
+
"""Tests for get_stats()."""
|
|
172
|
+
|
|
173
|
+
def test_empty_stats(self, curator_home: Path):
|
|
174
|
+
"""Stats on empty store returns zero counts."""
|
|
175
|
+
curator = MemoryCurator(curator_home)
|
|
176
|
+
stats = curator.get_stats()
|
|
177
|
+
assert stats["total"] == 0
|
|
178
|
+
|
|
179
|
+
def test_stats_with_memories(self, curator_home: Path):
|
|
180
|
+
"""Stats reflect stored memories."""
|
|
181
|
+
store(curator_home, "First memory", tags=["alpha"])
|
|
182
|
+
store(curator_home, "Second memory", tags=["beta"])
|
|
183
|
+
store(curator_home, "Untagged memory")
|
|
184
|
+
|
|
185
|
+
curator = MemoryCurator(curator_home)
|
|
186
|
+
stats = curator.get_stats()
|
|
187
|
+
|
|
188
|
+
assert stats["total"] == 3
|
|
189
|
+
assert stats["tag_coverage"] > 0.0
|
|
190
|
+
assert stats["avg_importance"] > 0.0
|
|
191
|
+
|
|
192
|
+
def test_top_tags(self, curator_home: Path):
|
|
193
|
+
"""Top tags are reported correctly."""
|
|
194
|
+
for _ in range(3):
|
|
195
|
+
store(curator_home, "PGP encryption test", tags=["pgp"])
|
|
196
|
+
store(curator_home, "Other stuff", tags=["misc"])
|
|
197
|
+
|
|
198
|
+
curator = MemoryCurator(curator_home)
|
|
199
|
+
stats = curator.get_stats()
|
|
200
|
+
|
|
201
|
+
tag_names = [t[0] for t in stats["top_tags"]]
|
|
202
|
+
assert "pgp" in tag_names
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class TestFullCuration:
|
|
206
|
+
"""End-to-end curation test."""
|
|
207
|
+
|
|
208
|
+
def test_full_pass(self, curator_home: Path):
|
|
209
|
+
"""Full curation pass runs all three phases."""
|
|
210
|
+
store(curator_home, "The skcapstone architecture uses pillars")
|
|
211
|
+
store(curator_home, "The skcapstone architecture uses pillars")
|
|
212
|
+
store(curator_home, "Security requires PGP encryption", tags=[])
|
|
213
|
+
|
|
214
|
+
curator = MemoryCurator(curator_home)
|
|
215
|
+
result = curator.curate()
|
|
216
|
+
|
|
217
|
+
assert result.total_scanned >= 3
|
|
218
|
+
assert isinstance(result, CurationResult)
|
|
@@ -22,6 +22,12 @@ from skcapstone.memory_engine import (
|
|
|
22
22
|
from skcapstone.models import MemoryEntry, MemoryLayer
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
@pytest.fixture(autouse=True)
|
|
26
|
+
def no_unified_backend(monkeypatch):
|
|
27
|
+
"""Disable the unified skmemory backend so tests use only file-based storage."""
|
|
28
|
+
monkeypatch.setattr("skcapstone.memory_engine._get_unified", lambda: None)
|
|
29
|
+
|
|
30
|
+
|
|
25
31
|
@pytest.fixture
|
|
26
32
|
def agent_home(tmp_path: Path) -> Path:
|
|
27
33
|
"""Create a temporary agent home with memory directories."""
|