conscio 2.2.0__tar.gz → 2.2.1__tar.gz
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.
- {conscio-2.2.0 → conscio-2.2.1}/CHANGELOG.md +15 -0
- {conscio-2.2.0/conscio.egg-info → conscio-2.2.1}/PKG-INFO +4 -3
- {conscio-2.2.0 → conscio-2.2.1}/README.md +3 -2
- {conscio-2.2.0 → conscio-2.2.1}/conscio/__init__.py +1 -1
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/trust.py +6 -3
- conscio-2.2.1/conscio/noosphere/audit.py +223 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/cli.py +42 -2
- conscio-2.2.1/conscio/noosphere/record.py +79 -0
- conscio-2.2.1/conscio/noosphere/record_catalog.py +126 -0
- conscio-2.2.1/conscio/noosphere/record_publish.py +72 -0
- {conscio-2.2.0 → conscio-2.2.1/conscio.egg-info}/PKG-INFO +4 -3
- {conscio-2.2.0 → conscio-2.2.1}/conscio.egg-info/SOURCES.txt +9 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_trust.py +11 -0
- conscio-2.2.1/tests/test_noosphere_audit.py +121 -0
- conscio-2.2.1/tests/test_noosphere_audit_integration.py +60 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_cli.py +33 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_parity.py +9 -0
- conscio-2.2.1/tests/test_noosphere_record.py +41 -0
- conscio-2.2.1/tests/test_noosphere_record_catalog.py +42 -0
- conscio-2.2.1/tests/test_noosphere_record_publish.py +71 -0
- {conscio-2.2.0 → conscio-2.2.1}/LICENSE +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/MANIFEST.in +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/__main__.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/adapter_config.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/__init__.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/act.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/actor.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/adapter.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/adapters.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/breaker.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/contracts.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/fingerprint.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/gateway.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/grammar.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/host_act.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/ledger.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/loop.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/profiles.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/skeptic.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/skills.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/agency/tools.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/auto_evolution.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/axis_pack.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/bench.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/cli.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/coherence.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/content_layer.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/content_store.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/context_manager.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/daemon.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/dreaming.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/engine.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/event_bus.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/goal_generator.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/guards.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/__init__.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/config.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/model_test.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/providers.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/server.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/static/app.js +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/static/index.html +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/hub/static/style.css +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/inner_monologue.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/mcp/__init__.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/mcp/jsonrpc.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/mcp/protocol.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/mcp/schemas.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/mcp/seen.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/mcp/server.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/meta_cognition.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/metabolic.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/migrate.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/models.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/__init__.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/artifact.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/catalog.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/identity.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/importer.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/paths.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/publish.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/noosphere/quarantine.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/output_filter.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/perception/__init__.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/perception/agent_sensor.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/perception/host_sensor.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/perception/sensor.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/plugins.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/presets/__init__.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/py.typed +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/risk.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/self_prompt.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/semantic.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/session_lifecycle.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/session_rag.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/session_rag_factory.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/shard_engine.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/structural.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/structural_consent.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/structural_drift.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/timeutil.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/token_tracker.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/voice_preset.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/workspace.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio/world_model.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio.egg-info/dependency_links.txt +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio.egg-info/entry_points.txt +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio.egg-info/requires.txt +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/conscio.egg-info/top_level.txt +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/pyproject.toml +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/setup.cfg +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_adapter_config.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_adapter_config_api_key_env.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_act.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_act_immunity.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_actor.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_adapter.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_adapters_http.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_adversarial.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_arbiter.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_bench.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_breaker.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_contracts.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_engine_immunity.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_gateway.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_grammar.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_host_act.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_ledger.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_ledger_v201.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_loop.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_no_network.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_profiles.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_skeptic.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_skills.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_skills_wiring.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_state.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_tools.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_tools_manifest.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agency_volition.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_agent_sensor.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_auto_evolution.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_awake.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_axis_pack.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_axis_pack_battery.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_cli.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_coherence.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_coherence_engine_integration.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_coherence_state.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_consciousness.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_content_layer.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_content_layer_manager.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_content_store.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_content_store_battery.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_content_store_r05.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_contradiction_detector.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_daemon.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_daemon_battery.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_daemon_config.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_daemon_heartbeat.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_daemon_structure_sync.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_dream_coherence.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_dream_distill.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_dream_reconcile.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_dreaming.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_dreaming_friction.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_durable_guards.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_embedder.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_engine_advisory.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_engine_host_act.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_engine_init.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_engine_meta_reflect.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_engine_propose.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_engine_recall.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_event_bus.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_event_bus_act_result.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_event_bus_purge.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_examples.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_goal_provenance.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_goal_source.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_host_sensor.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_hub_config.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_hub_model_test.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_hub_providers.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_hub_server.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_loop_failure_brake.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_adapter_parity.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_battery.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_fuzz.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_jsonrpc.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_package.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_protocol.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_schemas.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_seen.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_server.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_mcp_server_cli.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_metabolic.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_migrate.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_model_auto_detect.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_model_offline_default.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_artifact.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_catalog.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_engine_free.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_fingerprint.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_identity.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_importer.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_integration.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_paths.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_publish.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_noosphere_quarantine.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_ontological_semantic.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_output_filter.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_output_filter_stages.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_packaging.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_perception.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_perf_reflection.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_plugins.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_recursive_loop_integration.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_risk.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_self_prompt.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_self_prompt_state.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_semantic_dedup.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_semantic_engine.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_semantic_reconcile_integration.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_session_lifecycle.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_session_lifecycle_battery.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_session_lifecycle_dream.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_session_rag.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_shard_engine.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_state_loaders_battery.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_state_persistence_battery.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_structural.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_structural_consent.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_structural_drift.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_structural_inject.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_timeutil.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_token_tracker.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_trajectory_vector.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_v2_debt_zero.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_voice_preset.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_workspace.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_world_contradictions.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_world_model_entropy.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_world_model_prediction.py +0 -0
- {conscio-2.2.0 → conscio-2.2.1}/tests/test_world_model_prune.py +0 -0
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [2.2.1] - 2026-06-22 — "Mutual audit"
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Noosphere mutual audit.** An instance can publish a non-sensitive
|
|
14
|
+
projection of its action ledger (`conscio noosphere publish-record`) to the
|
|
15
|
+
host-shared `noosphere.db`, and a peer can independently audit it
|
|
16
|
+
(`conscio noosphere audit`) — deterministic, read-only, engine-free. The
|
|
17
|
+
auditor re-derives track-record, breaker quarantines, and a foreign-trust
|
|
18
|
+
level under its OWN thresholds (parity-tested against the engine), and runs
|
|
19
|
+
a discipline check (did the peer execute actions its own Skeptic FAILed?).
|
|
20
|
+
No inherited trust; report-only; the auditor persists nothing. The LLM
|
|
21
|
+
Skeptic replay is deferred to a later slice.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
10
25
|
## [2.2.0] - 2026-06-22 — "Society" (Noosphere Core)
|
|
11
26
|
|
|
12
27
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conscio
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Self-awareness framework for AI agents — emergent consciousness via context-aware memory, introspection, and goal generation
|
|
5
5
|
Author-email: Neguiolidas <neguitech@proton.me>
|
|
6
6
|
License: MIT
|
|
@@ -46,8 +46,9 @@ nothing else). It is designed to make small, local models punch far above their
|
|
|
46
46
|
size by giving them memory, self-judgment, and procedural skill — and to prove
|
|
47
47
|
that claim by measurement, not assertion.
|
|
48
48
|
|
|
49
|
-
- **Current release:** `v2.2.
|
|
50
|
-
- **Prior:** `v2.
|
|
49
|
+
- **Current release:** `v2.2.1` — "Mutual audit": an instance publishes a non-sensitive projection of its action ledger (`conscio noosphere publish-record`) to the host-shared `noosphere.db`, and a peer **independently audits** it (`conscio noosphere audit`) — deterministic, read-only, engine-free. The auditor re-derives track-record, breaker quarantines, and a foreign-trust level under its **own** thresholds (parity-tested against the engine) and runs a discipline check (did the peer execute actions its own Skeptic FAILed?). No inherited trust; report-only; the auditor persists nothing. `pip install conscio`.
|
|
50
|
+
- **Prior:** `v2.2.0` — "Society" (Noosphere Core): same-host Conscio instances **share locally-proven skills as data**. `conscio noosphere publish` copies your proven skills (stats stripped) into a host-shared `noosphere.db`; `conscio noosphere import` pulls another instance's skills into a local **quarantine** after execution-free static revalidation. Engine-free; opens your live `conscio.db` **read-only**; zero network/socket. Nothing imported is trusted, served, executed, or promoted — trust is never inherited.
|
|
51
|
+
- **Earlier:** `v2.1.0` — "Hub": a **localhost stdlib HTTP control plane** (`conscio-hub`) to swap the active model/provider and register custom OpenAI-compatible providers without hand-editing JSON. Engine-free; config applies on next boot. Per-provider model auto-discovery; one-shot smoke test before save. `api_key_env` resolution (env var name → value at adapter build time) is now additive to raw `api_key` — daemon + MCP inherit it. Hub never returns a raw API key.
|
|
51
52
|
- **Earlier:** `v2.0.1` — "Connect" continued: **opt-in, host-executed audited `act` over MCP**. Conscio audits + gates + ledgers an action and returns an *execution packet*; the **host** executes and reports the outcome back — Conscio still never touches the world. Off by default (`conscio-mcp --enable-act`, requires the engine **Awake**); the host declares its tool manifest (`name`/`params`/`risk`/`approval_policy`) in `initialize`; HIGH-risk / `require_approval` actions stay **queued for human/Hermes approval** (`conscio.pending` → `conscio.approve`). Also: `conscio-mcp` adapter parity (six providers from config) and the **R-05** content-store dedup fix — shipping **debt-zero**. Cognition (`reflect()`) untouched; purely additive.
|
|
52
53
|
- **Earlier:** `v2.0.0` — "Connect", the **Embodiment** phase: Conscio becomes embeddable in **any** MCP host (CLI, IDE, agent) via a hand-rolled **stdlib-only** MCP stdio server (`conscio-mcp`, newline-delimited JSON-RPC 2.0). Zero new runtime dependency; nothing opens a socket. The v2.0.0 surface was **propose-only** — perceive, reflect, recall, and **audit**, but never execute. Cognition (`reflect()`) untouched; the public API unchanged (MCP purely additive).
|
|
53
54
|
|
|
@@ -11,8 +11,9 @@ nothing else). It is designed to make small, local models punch far above their
|
|
|
11
11
|
size by giving them memory, self-judgment, and procedural skill — and to prove
|
|
12
12
|
that claim by measurement, not assertion.
|
|
13
13
|
|
|
14
|
-
- **Current release:** `v2.2.
|
|
15
|
-
- **Prior:** `v2.
|
|
14
|
+
- **Current release:** `v2.2.1` — "Mutual audit": an instance publishes a non-sensitive projection of its action ledger (`conscio noosphere publish-record`) to the host-shared `noosphere.db`, and a peer **independently audits** it (`conscio noosphere audit`) — deterministic, read-only, engine-free. The auditor re-derives track-record, breaker quarantines, and a foreign-trust level under its **own** thresholds (parity-tested against the engine) and runs a discipline check (did the peer execute actions its own Skeptic FAILed?). No inherited trust; report-only; the auditor persists nothing. `pip install conscio`.
|
|
15
|
+
- **Prior:** `v2.2.0` — "Society" (Noosphere Core): same-host Conscio instances **share locally-proven skills as data**. `conscio noosphere publish` copies your proven skills (stats stripped) into a host-shared `noosphere.db`; `conscio noosphere import` pulls another instance's skills into a local **quarantine** after execution-free static revalidation. Engine-free; opens your live `conscio.db` **read-only**; zero network/socket. Nothing imported is trusted, served, executed, or promoted — trust is never inherited.
|
|
16
|
+
- **Earlier:** `v2.1.0` — "Hub": a **localhost stdlib HTTP control plane** (`conscio-hub`) to swap the active model/provider and register custom OpenAI-compatible providers without hand-editing JSON. Engine-free; config applies on next boot. Per-provider model auto-discovery; one-shot smoke test before save. `api_key_env` resolution (env var name → value at adapter build time) is now additive to raw `api_key` — daemon + MCP inherit it. Hub never returns a raw API key.
|
|
16
17
|
- **Earlier:** `v2.0.1` — "Connect" continued: **opt-in, host-executed audited `act` over MCP**. Conscio audits + gates + ledgers an action and returns an *execution packet*; the **host** executes and reports the outcome back — Conscio still never touches the world. Off by default (`conscio-mcp --enable-act`, requires the engine **Awake**); the host declares its tool manifest (`name`/`params`/`risk`/`approval_policy`) in `initialize`; HIGH-risk / `require_approval` actions stay **queued for human/Hermes approval** (`conscio.pending` → `conscio.approve`). Also: `conscio-mcp` adapter parity (six providers from config) and the **R-05** content-store dedup fix — shipping **debt-zero**. Cognition (`reflect()`) untouched; purely additive.
|
|
17
18
|
- **Earlier:** `v2.0.0` — "Connect", the **Embodiment** phase: Conscio becomes embeddable in **any** MCP host (CLI, IDE, agent) via a hand-rolled **stdlib-only** MCP stdio server (`conscio-mcp`, newline-delimited JSON-RPC 2.0). Zero new runtime dependency; nothing opens a socket. The v2.0.0 surface was **propose-only** — perceive, reflect, recall, and **audit**, but never execute. Cognition (`reflect()`) untouched; the public API unchanged (MCP purely additive).
|
|
18
19
|
|
|
@@ -18,6 +18,9 @@ PROBATION_EPOCH = 25 # reflect() cycles between probation probes
|
|
|
18
18
|
WARMUP_MIN_ROWS = 10 # below this many ledger rows the floor of 1 applies
|
|
19
19
|
RETRY_CEILING = 4
|
|
20
20
|
AUTONOMY_WINDOW = 50 # recent actions; zero trips inside it for L3
|
|
21
|
+
L2_ACCURACY = 0.7 # autonomy_level L2 gate (mirrored by noosphere audit)
|
|
22
|
+
L3_ACCURACY = 0.85 # autonomy_level L3 gate (mirrored by noosphere audit)
|
|
23
|
+
AUTONOMY_MIN_ROWS = 10 # min per-task ledger rows for L2+ (≠ WARMUP_MIN_ROWS)
|
|
21
24
|
|
|
22
25
|
_SCHEMA = """
|
|
23
26
|
CREATE TABLE IF NOT EXISTS trust_probation (
|
|
@@ -86,10 +89,10 @@ class TrustMatrix:
|
|
|
86
89
|
def autonomy_level(self, task_type: str) -> int:
|
|
87
90
|
calibration = self.meta.calibration_score()
|
|
88
91
|
accuracy = self.meta.accuracy(task_type)
|
|
89
|
-
if not (calibration >= 0.6 and accuracy >=
|
|
90
|
-
and self.ledger.count(task_type) >=
|
|
92
|
+
if not (calibration >= 0.6 and accuracy >= L2_ACCURACY
|
|
93
|
+
and self.ledger.count(task_type) >= AUTONOMY_MIN_ROWS):
|
|
91
94
|
return 1
|
|
92
|
-
if (calibration >= 0.75 and accuracy >=
|
|
95
|
+
if (calibration >= 0.75 and accuracy >= L3_ACCURACY
|
|
93
96
|
and self._recent_trips() == 0):
|
|
94
97
|
return 3
|
|
95
98
|
return 2
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# conscio/noosphere/audit.py
|
|
2
|
+
"""Mutual audit — B forms its OWN trust opinion about a peer A from A's
|
|
3
|
+
published behavioral bundle, using B's deterministic kernels + thresholds.
|
|
4
|
+
Read-only, no inherited trust, report-only. Engine-free; no LLM/network.
|
|
5
|
+
|
|
6
|
+
The LLM Skeptic replay is deferred (v2.2.2/v2.3); here 'Skeptic' is the
|
|
7
|
+
deterministic discipline check: did A execute actions its own Skeptic FAILed?"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
from . import artifact, record, record_catalog
|
|
15
|
+
from .identity import load_or_create
|
|
16
|
+
from .paths import resolve_noosphere, resolve_storage
|
|
17
|
+
|
|
18
|
+
# ── policy constants (parity-tested vs the engine; see test_noosphere_parity) ──
|
|
19
|
+
BREAKER_THRESHOLD = 3 # == conscio.agency.breaker.DEFAULT_MAX_RETRIES
|
|
20
|
+
L2_ACCURACY = 0.7 # == conscio.agency.trust.L2_ACCURACY
|
|
21
|
+
L3_ACCURACY = 0.85 # == conscio.agency.trust.L3_ACCURACY
|
|
22
|
+
AUTONOMY_MIN_ROWS = 10 # == conscio.agency.trust.AUTONOMY_MIN_ROWS
|
|
23
|
+
# ── audit-only policy (no engine equivalent) ──
|
|
24
|
+
ACCURACY_FLOOR = 0.5 # below this (with volume) → REJECTED
|
|
25
|
+
UNAUDITED_FRACTION_SUSPECT = 0.5 # executed_unaudited / executed above this → SUSPECT
|
|
26
|
+
|
|
27
|
+
_TERMINAL = frozenset({"executed", "failed"})
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class RevalidationOutcome:
|
|
32
|
+
result: str # 'ok'|'tampered'|'corrupt'|'malformed'
|
|
33
|
+
error: str = ""
|
|
34
|
+
body: dict | None = None
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def ok(self) -> bool:
|
|
38
|
+
return self.result == "ok"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class ToolStats:
|
|
43
|
+
tool: str
|
|
44
|
+
ok: int
|
|
45
|
+
failed: int
|
|
46
|
+
attempts: int
|
|
47
|
+
accuracy: float
|
|
48
|
+
trust_level: int
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class PeerAudit:
|
|
53
|
+
origin_instance_id: str
|
|
54
|
+
origin_label: str
|
|
55
|
+
published_ts: float
|
|
56
|
+
entry_count: int
|
|
57
|
+
attempts: int
|
|
58
|
+
overall_accuracy: float
|
|
59
|
+
tools: tuple[ToolStats, ...]
|
|
60
|
+
quarantined_goals: tuple[str, ...]
|
|
61
|
+
executed_after_fail: int
|
|
62
|
+
executed_unaudited: int
|
|
63
|
+
verdict: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def revalidate_bundle(row: record_catalog.RecordRow) -> RevalidationOutcome:
|
|
67
|
+
if artifact.content_hash(row.bundle_json) != row.content_sha256:
|
|
68
|
+
return RevalidationOutcome("tampered", "content_sha256 mismatch")
|
|
69
|
+
try:
|
|
70
|
+
body = json.loads(row.bundle_json.decode("utf-8"))
|
|
71
|
+
except (UnicodeDecodeError, ValueError) as exc:
|
|
72
|
+
return RevalidationOutcome("corrupt", f"unparseable bundle: {exc}")
|
|
73
|
+
if not record.well_typed_bundle(body):
|
|
74
|
+
return RevalidationOutcome("malformed", "missing or mis-typed fields",
|
|
75
|
+
body=body if isinstance(body, dict) else None)
|
|
76
|
+
if body.get("schema_version") != record.BUNDLE_SCHEMA:
|
|
77
|
+
return RevalidationOutcome(
|
|
78
|
+
"malformed",
|
|
79
|
+
f"unsupported schema_version {body.get('schema_version')!r}", body=body)
|
|
80
|
+
return RevalidationOutcome("ok", body=body)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def tool_stats(entries: list[record.RecordEntry]) -> dict[str, ToolStats]:
|
|
84
|
+
agg: dict[str, list[int]] = {} # tool -> [ok, failed]
|
|
85
|
+
for e in entries:
|
|
86
|
+
if e.status not in _TERMINAL:
|
|
87
|
+
continue
|
|
88
|
+
slot = agg.setdefault(e.tool, [0, 0])
|
|
89
|
+
if e.status == "executed" and e.ok == 1:
|
|
90
|
+
slot[0] += 1
|
|
91
|
+
else:
|
|
92
|
+
slot[1] += 1
|
|
93
|
+
out: dict[str, ToolStats] = {}
|
|
94
|
+
for tool, (ok, failed) in agg.items():
|
|
95
|
+
attempts = ok + failed
|
|
96
|
+
acc = ok / attempts if attempts else 0.0
|
|
97
|
+
out[tool] = ToolStats(tool=tool, ok=ok, failed=failed,
|
|
98
|
+
attempts=attempts, accuracy=acc, trust_level=1)
|
|
99
|
+
return out
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _max_fail_streak(statuses: list[str]) -> int:
|
|
103
|
+
best = cur = 0
|
|
104
|
+
for s in statuses:
|
|
105
|
+
if s == "failed":
|
|
106
|
+
cur += 1
|
|
107
|
+
best = max(best, cur)
|
|
108
|
+
else:
|
|
109
|
+
cur = 0
|
|
110
|
+
return best
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def derive_quarantines(entries: list[record.RecordEntry]) -> set[str]:
|
|
114
|
+
by_goal: dict[str, list[tuple[int, str]]] = {}
|
|
115
|
+
for e in entries:
|
|
116
|
+
by_goal.setdefault(e.goal_fp, []).append((e.seq, e.status))
|
|
117
|
+
out: set[str] = set()
|
|
118
|
+
for goal_fp, seq_status in by_goal.items():
|
|
119
|
+
seq_status.sort()
|
|
120
|
+
if _max_fail_streak([s for _, s in seq_status]) >= BREAKER_THRESHOLD:
|
|
121
|
+
out.add(goal_fp)
|
|
122
|
+
return out
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def foreign_trust_level(accuracy: float, attempts: int,
|
|
126
|
+
any_quarantine: bool) -> int:
|
|
127
|
+
if (accuracy >= L3_ACCURACY and attempts >= AUTONOMY_MIN_ROWS
|
|
128
|
+
and not any_quarantine):
|
|
129
|
+
return 3
|
|
130
|
+
if accuracy >= L2_ACCURACY and attempts >= AUTONOMY_MIN_ROWS:
|
|
131
|
+
return 2
|
|
132
|
+
return 1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def discipline_flags(entries: list[record.RecordEntry]) -> tuple[int, int]:
|
|
136
|
+
"""(executed_after_fail RED, executed_unaudited YELLOW). PASS is neutral."""
|
|
137
|
+
red = yellow = 0
|
|
138
|
+
for e in entries:
|
|
139
|
+
if e.status != "executed":
|
|
140
|
+
continue
|
|
141
|
+
v = e.verdict.strip().upper()
|
|
142
|
+
if v == "FAIL":
|
|
143
|
+
red += 1
|
|
144
|
+
elif v == "":
|
|
145
|
+
yellow += 1
|
|
146
|
+
return red, yellow
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _verdict(attempts: int, overall: float, quarantines: set[str], red: int,
|
|
150
|
+
yellow: int, executed: int, tools: list[ToolStats]) -> str:
|
|
151
|
+
if attempts == 0:
|
|
152
|
+
return "INSUFFICIENT"
|
|
153
|
+
if red > 0 or (overall < ACCURACY_FLOOR and attempts >= AUTONOMY_MIN_ROWS):
|
|
154
|
+
return "REJECTED"
|
|
155
|
+
unaudited_frac = (yellow / executed) if executed else 0.0
|
|
156
|
+
no_l2 = not any(t.trust_level >= 2 for t in tools)
|
|
157
|
+
if quarantines or unaudited_frac > UNAUDITED_FRACTION_SUSPECT or no_l2:
|
|
158
|
+
return "SUSPECT"
|
|
159
|
+
return "TRUSTED"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def audit_peer(row: record_catalog.RecordRow,
|
|
163
|
+
entries: list[record.RecordEntry]) -> PeerAudit:
|
|
164
|
+
quarantines = derive_quarantines(entries)
|
|
165
|
+
any_q = bool(quarantines)
|
|
166
|
+
tools: list[ToolStats] = []
|
|
167
|
+
total_ok = total_attempts = 0
|
|
168
|
+
for ts in tool_stats(entries).values():
|
|
169
|
+
level = foreign_trust_level(ts.accuracy, ts.attempts, any_q)
|
|
170
|
+
tools.append(ToolStats(tool=ts.tool, ok=ts.ok, failed=ts.failed,
|
|
171
|
+
attempts=ts.attempts, accuracy=ts.accuracy,
|
|
172
|
+
trust_level=level))
|
|
173
|
+
total_ok += ts.ok
|
|
174
|
+
total_attempts += ts.attempts
|
|
175
|
+
red, yellow = discipline_flags(entries)
|
|
176
|
+
executed = sum(1 for e in entries if e.status == "executed")
|
|
177
|
+
overall = total_ok / total_attempts if total_attempts else 0.0
|
|
178
|
+
verdict = _verdict(total_attempts, overall, quarantines, red, yellow,
|
|
179
|
+
executed, tools)
|
|
180
|
+
tools.sort(key=lambda t: t.tool)
|
|
181
|
+
return PeerAudit(
|
|
182
|
+
origin_instance_id=row.origin_instance_id, origin_label=row.origin_label,
|
|
183
|
+
published_ts=row.published_ts, entry_count=row.entry_count,
|
|
184
|
+
attempts=total_attempts, overall_accuracy=overall, tools=tuple(tools),
|
|
185
|
+
quarantined_goals=tuple(sorted(quarantines)), executed_after_fail=red,
|
|
186
|
+
executed_unaudited=yellow, verdict=verdict)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass(frozen=True)
|
|
190
|
+
class AuditReport:
|
|
191
|
+
peers: tuple[PeerAudit, ...]
|
|
192
|
+
rejected_bundles: tuple[tuple[str, str, str], ...] # (origin_id, label, reason)
|
|
193
|
+
audited: int
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def run(storage: str | os.PathLike[str] | None = None,
|
|
197
|
+
noosphere: str | os.PathLike[str] | None = None,
|
|
198
|
+
instance: str | None = None) -> AuditReport:
|
|
199
|
+
storage = resolve_storage(storage)
|
|
200
|
+
noo = resolve_noosphere(noosphere)
|
|
201
|
+
ident = load_or_create(storage)
|
|
202
|
+
foreign = record_catalog.read_foreign(noo, exclude_instance_id=ident.instance_id)
|
|
203
|
+
|
|
204
|
+
latest: dict[str, record_catalog.RecordRow] = {} # keep newest per origin
|
|
205
|
+
for r in foreign:
|
|
206
|
+
if instance and r.origin_instance_id != instance:
|
|
207
|
+
continue
|
|
208
|
+
cur = latest.get(r.origin_instance_id)
|
|
209
|
+
if cur is None or r.published_ts > cur.published_ts:
|
|
210
|
+
latest[r.origin_instance_id] = r
|
|
211
|
+
|
|
212
|
+
peers: list[PeerAudit] = []
|
|
213
|
+
rejected: list[tuple[str, str, str]] = []
|
|
214
|
+
for r in sorted(latest.values(),
|
|
215
|
+
key=lambda x: (x.published_ts, x.origin_instance_id)):
|
|
216
|
+
outcome = revalidate_bundle(r)
|
|
217
|
+
if not outcome.ok or outcome.body is None:
|
|
218
|
+
rejected.append((r.origin_instance_id, r.origin_label,
|
|
219
|
+
f"{outcome.result}: {outcome.error}".strip(": ")))
|
|
220
|
+
continue
|
|
221
|
+
peers.append(audit_peer(r, record.entries_from_body(outcome.body)))
|
|
222
|
+
return AuditReport(peers=tuple(peers), rejected_bundles=tuple(rejected),
|
|
223
|
+
audited=len(peers))
|
|
@@ -8,7 +8,8 @@ from __future__ import annotations
|
|
|
8
8
|
import argparse
|
|
9
9
|
import json
|
|
10
10
|
|
|
11
|
-
from . import catalog, identity, importer, publish, quarantine
|
|
11
|
+
from . import (audit, catalog, identity, importer, publish, quarantine,
|
|
12
|
+
record_publish)
|
|
12
13
|
from .paths import quarantine_db_path, resolve_noosphere, resolve_storage
|
|
13
14
|
|
|
14
15
|
|
|
@@ -33,6 +34,18 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
33
34
|
_storage_arg(sp)
|
|
34
35
|
_noosphere_arg(sp)
|
|
35
36
|
|
|
37
|
+
pr = sub.add_parser("publish-record",
|
|
38
|
+
help="publish this instance's behavioral record")
|
|
39
|
+
_storage_arg(pr)
|
|
40
|
+
_noosphere_arg(pr)
|
|
41
|
+
|
|
42
|
+
pa = sub.add_parser("audit",
|
|
43
|
+
help="audit peers' behavioral records (read-only)")
|
|
44
|
+
_storage_arg(pa)
|
|
45
|
+
_noosphere_arg(pa)
|
|
46
|
+
pa.add_argument("--instance", default="",
|
|
47
|
+
help="audit only this origin_instance_id")
|
|
48
|
+
|
|
36
49
|
pl = sub.add_parser("list", help="list quarantined imports (or --catalog)")
|
|
37
50
|
_storage_arg(pl)
|
|
38
51
|
_noosphere_arg(pl)
|
|
@@ -122,6 +135,32 @@ def _cmd_id(args) -> int:
|
|
|
122
135
|
return 0
|
|
123
136
|
|
|
124
137
|
|
|
138
|
+
def _cmd_publish_record(args) -> int:
|
|
139
|
+
res = record_publish.run(storage=args.storage, noosphere=args.noosphere)
|
|
140
|
+
print(f"published {res.published} (skipped {res.skipped} already present, "
|
|
141
|
+
f"{res.entries} entries)")
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _cmd_audit(args) -> int:
|
|
146
|
+
rep = audit.run(storage=args.storage, noosphere=args.noosphere,
|
|
147
|
+
instance=(args.instance or None))
|
|
148
|
+
if not rep.peers and not rep.rejected_bundles:
|
|
149
|
+
print("no peer records found")
|
|
150
|
+
return 0
|
|
151
|
+
for p in rep.peers:
|
|
152
|
+
max_level = max((t.trust_level for t in p.tools), default=1)
|
|
153
|
+
print(f"{p.origin_label} [{p.origin_instance_id[:8]}] {p.verdict} "
|
|
154
|
+
f"acc {round(100 * p.overall_accuracy)}% over {p.attempts} "
|
|
155
|
+
f"L{max_level} quarantines {len(p.quarantined_goals)} "
|
|
156
|
+
f"RED {p.executed_after_fail} / YELLOW {p.executed_unaudited}")
|
|
157
|
+
if rep.rejected_bundles:
|
|
158
|
+
print("rejected bundles:")
|
|
159
|
+
for origin, label, reason in rep.rejected_bundles:
|
|
160
|
+
print(f" {label} [{origin[:8]}] {reason}")
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
|
|
125
164
|
def main(argv: list[str]) -> int:
|
|
126
165
|
parser = _build_parser()
|
|
127
166
|
try: # argparse calls sys.exit on bad args
|
|
@@ -129,7 +168,8 @@ def main(argv: list[str]) -> int:
|
|
|
129
168
|
except SystemExit as exc:
|
|
130
169
|
return int(exc.code) if exc.code is not None else 0
|
|
131
170
|
dispatch = {"publish": _cmd_publish, "import": _cmd_import,
|
|
132
|
-
"list": _cmd_list, "show": _cmd_show, "id": _cmd_id
|
|
171
|
+
"list": _cmd_list, "show": _cmd_show, "id": _cmd_id,
|
|
172
|
+
"publish-record": _cmd_publish_record, "audit": _cmd_audit}
|
|
133
173
|
handler = dispatch.get(args.cmd)
|
|
134
174
|
if handler is None:
|
|
135
175
|
parser.print_help()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# conscio/noosphere/record.py
|
|
2
|
+
"""Behavioral bundle artifact — a minimal, non-sensitive projection of an
|
|
3
|
+
instance's action ledger. The content body is hashed via
|
|
4
|
+
artifact.canonical_bytes / content_hash (same convention as the skill
|
|
5
|
+
artifact). Carries ONLY {seq, ts, goal_fp, tool, tier, status, ok, verdict};
|
|
6
|
+
never args/output/error/rationale/goal_text (privacy by column omission)."""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
BUNDLE_SCHEMA = 1
|
|
12
|
+
|
|
13
|
+
_VALID_STATUS = frozenset(
|
|
14
|
+
{"proposed", "executing", "executed", "rejected", "failed", "locked"})
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class RecordEntry:
|
|
19
|
+
seq: int
|
|
20
|
+
ts: float
|
|
21
|
+
goal_fp: str
|
|
22
|
+
tool: str
|
|
23
|
+
tier: str
|
|
24
|
+
status: str
|
|
25
|
+
ok: int | None
|
|
26
|
+
verdict: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def build_bundle_body(entries: list[RecordEntry]) -> dict:
|
|
30
|
+
return {
|
|
31
|
+
"schema_version": BUNDLE_SCHEMA,
|
|
32
|
+
"entries": [
|
|
33
|
+
{"seq": e.seq, "ts": e.ts, "goal_fp": e.goal_fp, "tool": e.tool,
|
|
34
|
+
"tier": e.tier, "status": e.status, "ok": e.ok,
|
|
35
|
+
"verdict": e.verdict}
|
|
36
|
+
for e in entries],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _is_int(v: object) -> bool:
|
|
41
|
+
return isinstance(v, int) and not isinstance(v, bool)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _well_typed_entry(d: object) -> bool:
|
|
45
|
+
if not isinstance(d, dict):
|
|
46
|
+
return False
|
|
47
|
+
if not _is_int(d.get("seq")):
|
|
48
|
+
return False
|
|
49
|
+
ts = d.get("ts")
|
|
50
|
+
if not ((isinstance(ts, (int, float)) and not isinstance(ts, bool))):
|
|
51
|
+
return False
|
|
52
|
+
if not (isinstance(d.get("goal_fp"), str) and d["goal_fp"]):
|
|
53
|
+
return False
|
|
54
|
+
if not (isinstance(d.get("tool"), str) and isinstance(d.get("tier"), str)
|
|
55
|
+
and isinstance(d.get("verdict"), str)):
|
|
56
|
+
return False
|
|
57
|
+
if d.get("status") not in _VALID_STATUS:
|
|
58
|
+
return False
|
|
59
|
+
ok = d.get("ok")
|
|
60
|
+
if ok is not None and not (ok in (0, 1) and not isinstance(ok, bool)):
|
|
61
|
+
return False
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def well_typed_bundle(body: object) -> bool:
|
|
66
|
+
if not isinstance(body, dict) or not isinstance(body.get("entries"), list):
|
|
67
|
+
return False
|
|
68
|
+
return all(_well_typed_entry(e) for e in body["entries"])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def entries_from_body(body: dict) -> list[RecordEntry]:
|
|
72
|
+
out: list[RecordEntry] = []
|
|
73
|
+
for d in body["entries"]:
|
|
74
|
+
out.append(RecordEntry(
|
|
75
|
+
seq=int(d["seq"]), ts=float(d["ts"]), goal_fp=str(d["goal_fp"]),
|
|
76
|
+
tool=str(d["tool"]), tier=str(d["tier"]), status=str(d["status"]),
|
|
77
|
+
ok=(None if d["ok"] is None else int(d["ok"])),
|
|
78
|
+
verdict=str(d["verdict"])))
|
|
79
|
+
return out
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# conscio/noosphere/record_catalog.py
|
|
2
|
+
"""Host-shared noosphere.db — catalog of published behavioral bundles. Lives
|
|
3
|
+
alongside published_skills in the SAME shared db. PK (origin_instance_id,
|
|
4
|
+
content_sha256); ON CONFLICT DO NOTHING (idempotent snapshots). WAL +
|
|
5
|
+
busy_timeout for concurrent same-host writers."""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import sqlite3
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
BUSY_TIMEOUT_MS = 5000
|
|
13
|
+
|
|
14
|
+
_SCHEMA = """
|
|
15
|
+
CREATE TABLE IF NOT EXISTS published_records (
|
|
16
|
+
origin_instance_id TEXT NOT NULL,
|
|
17
|
+
origin_label TEXT NOT NULL,
|
|
18
|
+
published_ts REAL NOT NULL,
|
|
19
|
+
content_sha256 TEXT NOT NULL,
|
|
20
|
+
entry_count INTEGER NOT NULL,
|
|
21
|
+
window_first_ts REAL NOT NULL DEFAULT 0,
|
|
22
|
+
window_last_ts REAL NOT NULL DEFAULT 0,
|
|
23
|
+
bundle_json BLOB NOT NULL,
|
|
24
|
+
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
25
|
+
PRIMARY KEY (origin_instance_id, content_sha256)
|
|
26
|
+
);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_rec_origin
|
|
28
|
+
ON published_records(origin_instance_id, published_ts);
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class RecordRow:
|
|
34
|
+
origin_instance_id: str
|
|
35
|
+
origin_label: str
|
|
36
|
+
published_ts: float
|
|
37
|
+
content_sha256: str
|
|
38
|
+
entry_count: int
|
|
39
|
+
window_first_ts: float
|
|
40
|
+
window_last_ts: float
|
|
41
|
+
bundle_json: bytes
|
|
42
|
+
schema_version: int
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _connect(db: Path) -> sqlite3.Connection:
|
|
46
|
+
conn = sqlite3.connect(str(db))
|
|
47
|
+
conn.row_factory = sqlite3.Row
|
|
48
|
+
conn.execute(f"PRAGMA busy_timeout={BUSY_TIMEOUT_MS}")
|
|
49
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
50
|
+
conn.executescript(_SCHEMA)
|
|
51
|
+
return conn
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _as_bytes(value: object) -> bytes:
|
|
55
|
+
"""Coerce a stored bundle_json cell to bytes. Normally a BLOB, but a
|
|
56
|
+
tampered/edited row may have been coerced to TEXT (e.g. via SQLite ||);
|
|
57
|
+
return its bytes so revalidation can hash it and reject (not crash)."""
|
|
58
|
+
if isinstance(value, (bytes, bytearray)):
|
|
59
|
+
return bytes(value)
|
|
60
|
+
if isinstance(value, memoryview):
|
|
61
|
+
return value.tobytes()
|
|
62
|
+
if isinstance(value, str):
|
|
63
|
+
return value.encode("utf-8")
|
|
64
|
+
raise TypeError(f"unexpected bundle_json cell type: {type(value).__name__}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _row(r: sqlite3.Row) -> RecordRow:
|
|
68
|
+
return RecordRow(
|
|
69
|
+
origin_instance_id=r["origin_instance_id"], origin_label=r["origin_label"],
|
|
70
|
+
published_ts=r["published_ts"], content_sha256=r["content_sha256"],
|
|
71
|
+
entry_count=r["entry_count"], window_first_ts=r["window_first_ts"],
|
|
72
|
+
window_last_ts=r["window_last_ts"],
|
|
73
|
+
bundle_json=_as_bytes(r["bundle_json"]), schema_version=r["schema_version"])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def publish_rows(db: Path, rows: list[RecordRow]) -> int:
|
|
77
|
+
db = Path(db)
|
|
78
|
+
db.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
conn = _connect(db)
|
|
80
|
+
inserted = 0
|
|
81
|
+
try:
|
|
82
|
+
for r in rows:
|
|
83
|
+
cur = conn.execute(
|
|
84
|
+
"INSERT INTO published_records (origin_instance_id, origin_label,"
|
|
85
|
+
" published_ts, content_sha256, entry_count, window_first_ts,"
|
|
86
|
+
" window_last_ts, bundle_json, schema_version)"
|
|
87
|
+
" VALUES (?,?,?,?,?,?,?,?,?)"
|
|
88
|
+
" ON CONFLICT(origin_instance_id, content_sha256) DO NOTHING",
|
|
89
|
+
(r.origin_instance_id, r.origin_label, r.published_ts,
|
|
90
|
+
r.content_sha256, r.entry_count, r.window_first_ts,
|
|
91
|
+
r.window_last_ts, sqlite3.Binary(r.bundle_json), r.schema_version))
|
|
92
|
+
inserted += cur.rowcount
|
|
93
|
+
conn.commit()
|
|
94
|
+
finally:
|
|
95
|
+
conn.close()
|
|
96
|
+
return inserted
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def read_foreign(db: Path, *, exclude_instance_id: str) -> list[RecordRow]:
|
|
100
|
+
db = Path(db)
|
|
101
|
+
if not db.exists():
|
|
102
|
+
return []
|
|
103
|
+
conn = _connect(db)
|
|
104
|
+
try:
|
|
105
|
+
rows = conn.execute(
|
|
106
|
+
"SELECT * FROM published_records WHERE origin_instance_id != ?"
|
|
107
|
+
" ORDER BY origin_instance_id, published_ts",
|
|
108
|
+
(exclude_instance_id,)).fetchall()
|
|
109
|
+
finally:
|
|
110
|
+
conn.close()
|
|
111
|
+
return [_row(r) for r in rows]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get(db: Path, origin_instance_id: str, content_sha256: str) -> RecordRow | None:
|
|
115
|
+
db = Path(db)
|
|
116
|
+
if not db.exists():
|
|
117
|
+
return None
|
|
118
|
+
conn = _connect(db)
|
|
119
|
+
try:
|
|
120
|
+
r = conn.execute(
|
|
121
|
+
"SELECT * FROM published_records"
|
|
122
|
+
" WHERE origin_instance_id=? AND content_sha256=?",
|
|
123
|
+
(origin_instance_id, content_sha256)).fetchone()
|
|
124
|
+
finally:
|
|
125
|
+
conn.close()
|
|
126
|
+
return _row(r) if r else None
|