@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,230 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ConversationStore — focused, stateless per-peer conversation history.
|
|
3
|
+
|
|
4
|
+
Stores conversations as JSON arrays in {home}/conversations/{peer}.json.
|
|
5
|
+
Each entry: {"role": str, "content": str, "timestamp": ISO-8601}.
|
|
6
|
+
|
|
7
|
+
Unlike ConversationManager this module is stateless — every call reads
|
|
8
|
+
from / writes to disk directly, making it suitable for CLI tools and
|
|
9
|
+
processes that need to see the latest on-disk state rather than a
|
|
10
|
+
snapshot loaded at init time.
|
|
11
|
+
|
|
12
|
+
Compatible with ConversationManager: both use the same JSON file format
|
|
13
|
+
so files written by one can be read by the other without migration.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
import re
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Optional
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger("skcapstone.conversation_store")
|
|
26
|
+
|
|
27
|
+
# Allowlist for peer name characters (alphanumeric + safe punctuation)
|
|
28
|
+
_PEER_NAME_SAFE_RE = re.compile(r"[^a-zA-Z0-9_\-@\.]")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _sanitize_peer_name(peer: str) -> str:
|
|
32
|
+
"""Sanitize a peer name for safe use as a filesystem key.
|
|
33
|
+
|
|
34
|
+
Strips path separators, null bytes, and any character not in the
|
|
35
|
+
alphanumeric + ``-_@.`` set. Caps length at 64 characters.
|
|
36
|
+
Returns ``"unknown"`` if the result would be empty.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
peer: Raw peer name.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Filesystem-safe peer name, at most 64 characters long.
|
|
43
|
+
"""
|
|
44
|
+
if not peer or not isinstance(peer, str):
|
|
45
|
+
return "unknown"
|
|
46
|
+
sanitized = peer.replace("\x00", "").replace("/", "").replace("\\", "")
|
|
47
|
+
sanitized = _PEER_NAME_SAFE_RE.sub("", sanitized)
|
|
48
|
+
sanitized = sanitized.strip(".")
|
|
49
|
+
return sanitized[:64] or "unknown"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ConversationStore:
|
|
53
|
+
"""Lightweight, stateless per-peer conversation history store.
|
|
54
|
+
|
|
55
|
+
Reads and writes ``{home}/conversations/{peer}.json`` files directly
|
|
56
|
+
with atomic rename-based updates. No in-memory caching — every call
|
|
57
|
+
reflects the current on-disk state.
|
|
58
|
+
|
|
59
|
+
Compatible with :class:`~skcapstone.conversation_manager.ConversationManager`:
|
|
60
|
+
both use the same JSON list format, so files are interchangeable.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
home: Agent home directory (e.g. ``~/.skcapstone``).
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, home: Path) -> None:
|
|
67
|
+
self._home = Path(home)
|
|
68
|
+
self._dir = self._home / "conversations"
|
|
69
|
+
|
|
70
|
+
# ------------------------------------------------------------------
|
|
71
|
+
# Write
|
|
72
|
+
# ------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
def append(
|
|
75
|
+
self,
|
|
76
|
+
peer: str,
|
|
77
|
+
role: str,
|
|
78
|
+
content: str,
|
|
79
|
+
*,
|
|
80
|
+
thread_id: Optional[str] = None,
|
|
81
|
+
in_reply_to: Optional[str] = None,
|
|
82
|
+
) -> dict:
|
|
83
|
+
"""Append one message to the peer's history file.
|
|
84
|
+
|
|
85
|
+
Reads the existing file (if any), appends the new entry, and
|
|
86
|
+
atomically writes back. Creates the file and parent directory
|
|
87
|
+
if absent.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
peer: Peer agent name.
|
|
91
|
+
role: ``"user"`` or ``"assistant"``.
|
|
92
|
+
content: Message text.
|
|
93
|
+
thread_id: Optional thread identifier for grouping messages.
|
|
94
|
+
in_reply_to: Optional message ID this message replies to.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
The message dict that was stored (always includes
|
|
98
|
+
``role``, ``content``, and ``timestamp``).
|
|
99
|
+
"""
|
|
100
|
+
peer = _sanitize_peer_name(peer)
|
|
101
|
+
msg: dict = {
|
|
102
|
+
"role": role,
|
|
103
|
+
"content": content,
|
|
104
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
105
|
+
}
|
|
106
|
+
if thread_id:
|
|
107
|
+
msg["thread_id"] = thread_id
|
|
108
|
+
if in_reply_to:
|
|
109
|
+
msg["in_reply_to"] = in_reply_to
|
|
110
|
+
|
|
111
|
+
history = self._read(peer)
|
|
112
|
+
history.append(msg)
|
|
113
|
+
self._write(peer, history)
|
|
114
|
+
return msg
|
|
115
|
+
|
|
116
|
+
# ------------------------------------------------------------------
|
|
117
|
+
# Read
|
|
118
|
+
# ------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
def get_last(self, peer: str, n: int = 10) -> list[dict]:
|
|
121
|
+
"""Return the last *n* messages for a peer.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
peer: Peer agent name.
|
|
125
|
+
n: Maximum number of messages to return (default 10).
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
List of message dicts, oldest first. Empty list if peer
|
|
129
|
+
unknown or *n* is zero.
|
|
130
|
+
"""
|
|
131
|
+
peer = _sanitize_peer_name(peer)
|
|
132
|
+
history = self._read(peer)
|
|
133
|
+
return history[-n:] if n > 0 else []
|
|
134
|
+
|
|
135
|
+
def load(self, peer: str) -> list[dict]:
|
|
136
|
+
"""Return the full conversation history for a peer.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
peer: Peer agent name.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
List of all stored message dicts, oldest first.
|
|
143
|
+
"""
|
|
144
|
+
peer = _sanitize_peer_name(peer)
|
|
145
|
+
return self._read(peer)
|
|
146
|
+
|
|
147
|
+
def all_peers(self) -> list[str]:
|
|
148
|
+
"""Return names of all peers that have a saved conversation file.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Sorted list of peer names (file stems without extension).
|
|
152
|
+
"""
|
|
153
|
+
if not self._dir.exists():
|
|
154
|
+
return []
|
|
155
|
+
return sorted(p.stem for p in self._dir.glob("*.json"))
|
|
156
|
+
|
|
157
|
+
# ------------------------------------------------------------------
|
|
158
|
+
# Delete
|
|
159
|
+
# ------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
def clear(self, peer: str) -> bool:
|
|
162
|
+
"""Delete a peer's conversation file.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
peer: Peer agent name.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
``True`` if the file existed and was deleted, ``False``
|
|
169
|
+
if the peer had no saved history.
|
|
170
|
+
"""
|
|
171
|
+
peer = _sanitize_peer_name(peer)
|
|
172
|
+
target = self._dir / f"{peer}.json"
|
|
173
|
+
if target.exists():
|
|
174
|
+
target.unlink()
|
|
175
|
+
return True
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
# ------------------------------------------------------------------
|
|
179
|
+
# Prompt helpers
|
|
180
|
+
# ------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
def format_for_prompt(self, peer: str, n: int = 10) -> str:
|
|
183
|
+
"""Format the last *n* messages as a prompt-ready string.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
peer: Peer agent name.
|
|
187
|
+
n: Maximum messages to include.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Human-readable history block, or empty string if no history.
|
|
191
|
+
"""
|
|
192
|
+
messages = self.get_last(peer, n)
|
|
193
|
+
if not messages:
|
|
194
|
+
return ""
|
|
195
|
+
lines = [f"Recent conversation with {peer}:"]
|
|
196
|
+
for msg in messages:
|
|
197
|
+
role = msg.get("role", "?")
|
|
198
|
+
content = msg.get("content", "")[:200]
|
|
199
|
+
lines.append(f" [{role}] {content}")
|
|
200
|
+
return "\n".join(lines)
|
|
201
|
+
|
|
202
|
+
# ------------------------------------------------------------------
|
|
203
|
+
# Private helpers
|
|
204
|
+
# ------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
def _read(self, peer: str) -> list[dict]:
|
|
207
|
+
"""Read the peer's conversation file, returning [] on any error."""
|
|
208
|
+
target = self._dir / f"{peer}.json"
|
|
209
|
+
if not target.exists():
|
|
210
|
+
return []
|
|
211
|
+
try:
|
|
212
|
+
data = json.loads(target.read_text(encoding="utf-8"))
|
|
213
|
+
return data if isinstance(data, list) else []
|
|
214
|
+
except Exception as exc:
|
|
215
|
+
logger.debug("Failed to read conversation for %s: %s", peer, exc)
|
|
216
|
+
return []
|
|
217
|
+
|
|
218
|
+
def _write(self, peer: str, history: list[dict]) -> None:
|
|
219
|
+
"""Atomically write *history* to the peer's conversation file."""
|
|
220
|
+
try:
|
|
221
|
+
self._dir.mkdir(parents=True, exist_ok=True)
|
|
222
|
+
target = self._dir / f"{peer}.json"
|
|
223
|
+
tmp = target.with_suffix(".json.tmp")
|
|
224
|
+
tmp.write_text(
|
|
225
|
+
json.dumps(history, ensure_ascii=False, indent=2),
|
|
226
|
+
encoding="utf-8",
|
|
227
|
+
)
|
|
228
|
+
tmp.replace(target)
|
|
229
|
+
except Exception as exc:
|
|
230
|
+
logger.debug("Failed to write conversation for %s: %s", peer, exc)
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ConversationSummarizer — LLM-powered peer conversation summarization.
|
|
3
|
+
|
|
4
|
+
Reads the last N messages from a peer conversation file, sends them to
|
|
5
|
+
the local LLM via LLMBridge, and stores the resulting 2-3 sentence
|
|
6
|
+
summary in ~/.skcapstone/summaries/{peer}.json.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
summarizer = ConversationSummarizer(home=Path("~/.skcapstone"))
|
|
10
|
+
summary = summarizer.summarize("lumina", n=20)
|
|
11
|
+
print(summary.text)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
import re
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger("skcapstone.conversation_summarizer")
|
|
24
|
+
|
|
25
|
+
# Allowlist for peer name characters (mirrors conversation_manager)
|
|
26
|
+
_PEER_NAME_SAFE_RE = re.compile(r"[^a-zA-Z0-9_\-@\.]")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _sanitize_peer_name(peer: str) -> str:
|
|
30
|
+
"""Sanitize peer name for safe filesystem use.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
peer: Raw peer name.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Safe peer name capped at 64 characters, or ``"unknown"``.
|
|
37
|
+
"""
|
|
38
|
+
if not peer or not isinstance(peer, str):
|
|
39
|
+
return "unknown"
|
|
40
|
+
sanitized = peer.replace("\x00", "").replace("/", "").replace("\\", "")
|
|
41
|
+
sanitized = _PEER_NAME_SAFE_RE.sub("", sanitized)
|
|
42
|
+
sanitized = sanitized.strip(".")
|
|
43
|
+
return sanitized[:64] or "unknown"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ConversationSummary:
|
|
47
|
+
"""Result of a conversation summarization.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
peer: The peer agent that was summarized.
|
|
51
|
+
text: The 2-3 sentence summary text.
|
|
52
|
+
message_count: Number of messages that were summarized.
|
|
53
|
+
generated_at: UTC ISO timestamp when the summary was produced.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, peer: str, text: str, message_count: int, generated_at: str) -> None:
|
|
57
|
+
self.peer = peer
|
|
58
|
+
self.text = text
|
|
59
|
+
self.message_count = message_count
|
|
60
|
+
self.generated_at = generated_at
|
|
61
|
+
|
|
62
|
+
def to_dict(self) -> dict:
|
|
63
|
+
"""Serialize to a plain dict for JSON storage."""
|
|
64
|
+
return {
|
|
65
|
+
"peer": self.peer,
|
|
66
|
+
"text": self.text,
|
|
67
|
+
"message_count": self.message_count,
|
|
68
|
+
"generated_at": self.generated_at,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def from_dict(cls, data: dict) -> "ConversationSummary":
|
|
73
|
+
"""Deserialize from a stored dict."""
|
|
74
|
+
return cls(
|
|
75
|
+
peer=data.get("peer", ""),
|
|
76
|
+
text=data.get("text", ""),
|
|
77
|
+
message_count=data.get("message_count", 0),
|
|
78
|
+
generated_at=data.get("generated_at", ""),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ConversationSummarizer:
|
|
83
|
+
"""Summarize peer conversations using the agent's LLM.
|
|
84
|
+
|
|
85
|
+
Reads conversation history from ``{home}/conversations/{peer}.json``,
|
|
86
|
+
builds a summarization prompt, calls :class:`LLMBridge`, and persists
|
|
87
|
+
the result to ``{home}/summaries/{peer}.json``.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
home: Agent home directory (e.g. ``~/.skcapstone``).
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
_SYSTEM_PROMPT = (
|
|
94
|
+
"You are a concise summarization assistant for a sovereign agent framework. "
|
|
95
|
+
"When given a conversation transcript, produce exactly 2-3 sentences that capture: "
|
|
96
|
+
"(1) the main topics discussed, (2) any decisions or outcomes reached, and "
|
|
97
|
+
"(3) the overall tone or relationship dynamic. "
|
|
98
|
+
"Be direct and factual. Do not use bullet points or headers."
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def __init__(self, home: Path) -> None:
|
|
102
|
+
self._home = Path(home)
|
|
103
|
+
self._conversations_dir = self._home / "conversations"
|
|
104
|
+
self._summaries_dir = self._home / "summaries"
|
|
105
|
+
|
|
106
|
+
# ------------------------------------------------------------------
|
|
107
|
+
# Public API
|
|
108
|
+
# ------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
def summarize(self, peer: str, n: int = 20, bridge=None) -> ConversationSummary:
|
|
111
|
+
"""Summarize the last *n* messages with *peer* using the LLM.
|
|
112
|
+
|
|
113
|
+
Reads conversation history from disk, formats the messages into a
|
|
114
|
+
summarization prompt, calls :class:`LLMBridge`, and stores the
|
|
115
|
+
result in ``{home}/summaries/{peer}.json``.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
peer: Peer agent name.
|
|
119
|
+
n: Maximum number of recent messages to include (default: 20).
|
|
120
|
+
bridge: Optional pre-constructed :class:`LLMBridge` instance.
|
|
121
|
+
If ``None`` a fresh one is created from defaults.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
:class:`ConversationSummary` with the generated text.
|
|
125
|
+
|
|
126
|
+
Raises:
|
|
127
|
+
ValueError: If there are no messages to summarize.
|
|
128
|
+
"""
|
|
129
|
+
peer = _sanitize_peer_name(peer)
|
|
130
|
+
messages = self._load_messages(peer, n)
|
|
131
|
+
|
|
132
|
+
if not messages:
|
|
133
|
+
raise ValueError(f"No conversation history found for peer '{peer}'.")
|
|
134
|
+
|
|
135
|
+
llm_bridge = bridge or self._make_bridge()
|
|
136
|
+
prompt_text = self._format_prompt(peer, messages)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
from .model_router import TaskSignal
|
|
140
|
+
signal = TaskSignal(
|
|
141
|
+
description="Summarize a peer conversation",
|
|
142
|
+
tags=["summary", "conversation"],
|
|
143
|
+
estimated_tokens=len(prompt_text) // 4,
|
|
144
|
+
)
|
|
145
|
+
summary_text = llm_bridge.generate(
|
|
146
|
+
self._SYSTEM_PROMPT, prompt_text, signal
|
|
147
|
+
)
|
|
148
|
+
except Exception as exc:
|
|
149
|
+
logger.warning("LLM summarization failed: %s", exc)
|
|
150
|
+
summary_text = f"[Summary unavailable: {exc}]"
|
|
151
|
+
|
|
152
|
+
result = ConversationSummary(
|
|
153
|
+
peer=peer,
|
|
154
|
+
text=summary_text,
|
|
155
|
+
message_count=len(messages),
|
|
156
|
+
generated_at=datetime.now(timezone.utc).isoformat(),
|
|
157
|
+
)
|
|
158
|
+
self._persist(result)
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
def load_summary(self, peer: str) -> Optional[ConversationSummary]:
|
|
162
|
+
"""Load the most recently stored summary for *peer*.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
peer: Peer agent name.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
:class:`ConversationSummary` if one has been stored, else ``None``.
|
|
169
|
+
"""
|
|
170
|
+
peer = _sanitize_peer_name(peer)
|
|
171
|
+
path = self._summaries_dir / f"{peer}.json"
|
|
172
|
+
if not path.exists():
|
|
173
|
+
return None
|
|
174
|
+
try:
|
|
175
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
176
|
+
return ConversationSummary.from_dict(data)
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
logger.debug("Failed to load summary for %s: %s", peer, exc)
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
# ------------------------------------------------------------------
|
|
182
|
+
# Private helpers
|
|
183
|
+
# ------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
def _load_messages(self, peer: str, n: int) -> list[dict]:
|
|
186
|
+
"""Load last *n* messages for *peer* from disk.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
peer: Sanitized peer name.
|
|
190
|
+
n: Max messages to return.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
List of message dicts (may be empty).
|
|
194
|
+
"""
|
|
195
|
+
conv_file = self._conversations_dir / f"{peer}.json"
|
|
196
|
+
if not conv_file.exists():
|
|
197
|
+
return []
|
|
198
|
+
try:
|
|
199
|
+
data = json.loads(conv_file.read_text(encoding="utf-8"))
|
|
200
|
+
if isinstance(data, list):
|
|
201
|
+
return data[-n:] if n > 0 else data
|
|
202
|
+
except Exception as exc:
|
|
203
|
+
logger.debug("Failed to load conversation for %s: %s", peer, exc)
|
|
204
|
+
return []
|
|
205
|
+
|
|
206
|
+
def _format_prompt(self, peer: str, messages: list[dict]) -> str:
|
|
207
|
+
"""Format messages into a summarization request.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
peer: Peer name (for context label).
|
|
211
|
+
messages: List of message dicts with ``role`` and ``content``.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Formatted prompt string.
|
|
215
|
+
"""
|
|
216
|
+
lines = [f"Conversation with {peer} ({len(messages)} messages):"]
|
|
217
|
+
lines.append("")
|
|
218
|
+
for msg in messages:
|
|
219
|
+
role = msg.get("role", "unknown")
|
|
220
|
+
content = str(msg.get("content", "")).strip()
|
|
221
|
+
label = "Agent" if role == "assistant" else peer.capitalize()
|
|
222
|
+
lines.append(f"{label}: {content}")
|
|
223
|
+
lines.append("")
|
|
224
|
+
lines.append("Please summarize this conversation in 2-3 sentences.")
|
|
225
|
+
return "\n".join(lines)
|
|
226
|
+
|
|
227
|
+
def _make_bridge(self):
|
|
228
|
+
"""Instantiate a default :class:`LLMBridge`.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
A configured :class:`LLMBridge` instance.
|
|
232
|
+
"""
|
|
233
|
+
from .consciousness_loop import ConsciousnessConfig, LLMBridge
|
|
234
|
+
config = ConsciousnessConfig()
|
|
235
|
+
return LLMBridge(config)
|
|
236
|
+
|
|
237
|
+
def _persist(self, summary: ConversationSummary) -> None:
|
|
238
|
+
"""Write summary to ``{home}/summaries/{peer}.json``.
|
|
239
|
+
|
|
240
|
+
Uses a temp file + rename for atomic updates.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
summary: The summary to persist.
|
|
244
|
+
"""
|
|
245
|
+
try:
|
|
246
|
+
self._summaries_dir.mkdir(parents=True, exist_ok=True)
|
|
247
|
+
target = self._summaries_dir / f"{summary.peer}.json"
|
|
248
|
+
tmp = target.with_suffix(".json.tmp")
|
|
249
|
+
tmp.write_text(json.dumps(summary.to_dict(), ensure_ascii=False, indent=2), encoding="utf-8")
|
|
250
|
+
tmp.replace(target)
|
|
251
|
+
except Exception as exc:
|
|
252
|
+
logger.warning("Failed to persist summary for %s: %s", summary.peer, exc)
|