conscio 2.6.2__tar.gz → 2.7.0__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.6.2 → conscio-2.7.0}/CHANGELOG.md +39 -0
- {conscio-2.6.2/conscio.egg-info → conscio-2.7.0}/PKG-INFO +5 -3
- {conscio-2.6.2 → conscio-2.7.0}/README.md +4 -2
- {conscio-2.6.2 → conscio-2.7.0}/conscio/__init__.py +1 -1
- conscio-2.7.0/conscio/agency/relay_respond.py +81 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/daemon.py +39 -1
- {conscio-2.6.2 → conscio-2.7.0}/conscio/mcp/server.py +10 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/perception/relay_sensor.py +4 -2
- {conscio-2.6.2 → conscio-2.7.0/conscio.egg-info}/PKG-INFO +5 -3
- {conscio-2.6.2 → conscio-2.7.0}/conscio.egg-info/SOURCES.txt +4 -0
- conscio-2.7.0/tests/test_daemon_responder.py +58 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_auto_review.py +25 -0
- conscio-2.7.0/tests/test_relay_e2e.py +124 -0
- conscio-2.7.0/tests/test_relay_respond.py +139 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_relay_sensor.py +10 -0
- {conscio-2.6.2 → conscio-2.7.0}/LICENSE +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/MANIFEST.in +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/__main__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/adapter_config.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/act.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/actor.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/adapter.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/adapters.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/breaker.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/contracts.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/fingerprint.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/gateway.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/grammar.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/host_act.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/ledger.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/loop.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/profiles.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/promote.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/review_apply.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/skeptic.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/skills.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/tools.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/trial.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/agency/trust.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/auto_evolution.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/axis_pack.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/bench.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/cli.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/coherence.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/content_layer.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/content_store.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/context_manager.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/dreaming.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/engine.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/event_bus.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/goal_generator.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/guards.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/config.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/model_test.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/providers.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/server.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/static/app.js +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/static/index.html +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/hub/static/style.css +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/inner_monologue.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/liaison/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/liaison/mailbox.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/liaison/relay.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/liaison/review.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/mcp/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/mcp/jsonrpc.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/mcp/protocol.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/mcp/schemas.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/mcp/seen.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/meta_cognition.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/metabolic.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/migrate.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/models.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/artifact.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/audit.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/catalog.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/cli.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/identity.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/importer.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/paths.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/publish.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/quarantine.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/record.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/record_catalog.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/noosphere/record_publish.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/observatory/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/observatory/projection.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/observatory/server.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/observatory/society.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/observatory/static/app.js +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/observatory/static/index.html +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/observatory/static/style.css +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/output_filter.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/perception/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/perception/agent_sensor.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/perception/host_sensor.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/perception/sensor.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/plugins.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/presets/__init__.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/py.typed +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/risk.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/self_prompt.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/semantic.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/session_lifecycle.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/session_rag.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/session_rag_factory.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/shard_engine.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/structural.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/structural_consent.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/structural_drift.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/timeutil.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/token_tracker.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/voice_preset.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/workspace.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio/world_model.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio.egg-info/dependency_links.txt +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio.egg-info/entry_points.txt +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio.egg-info/requires.txt +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/conscio.egg-info/top_level.txt +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/pyproject.toml +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/setup.cfg +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_adapter_config.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_adapter_config_api_key_env.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_act.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_act_immunity.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_actor.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_adapter.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_adapters_http.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_adversarial.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_arbiter.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_bench.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_breaker.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_contracts.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_engine_immunity.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_gateway.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_grammar.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_host_act.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_ledger.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_ledger_v201.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_loop.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_no_network.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_profiles.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_promote.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_skeptic.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_skills.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_skills_wiring.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_state.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_tools.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_tools_manifest.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_trial.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_trust.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agency_volition.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_agent_sensor.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_auto_evolution.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_awake.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_axis_pack.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_axis_pack_battery.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_cli.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_cli_promote.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_cli_trial.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_coherence.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_coherence_engine_integration.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_coherence_state.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_consciousness.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_content_layer.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_content_layer_manager.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_content_store.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_content_store_battery.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_content_store_r05.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_contradiction_detector.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_daemon.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_daemon_battery.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_daemon_config.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_daemon_heartbeat.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_daemon_relay.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_daemon_structure_sync.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_dream_coherence.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_dream_distill.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_dream_reconcile.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_dreaming.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_dreaming_friction.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_durable_guards.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_embedder.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_advisory.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_host_act.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_init.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_meta_reflect.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_promote.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_propose.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_recall.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_engine_trial.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_event_bus.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_event_bus_act_result.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_event_bus_purge.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_examples.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_goal_provenance.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_goal_source.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_host_sensor.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_hub_config.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_hub_model_test.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_hub_providers.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_hub_server.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_liaison_bindings.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_liaison_engine_free.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_liaison_mailbox.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_liaison_relay.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_liaison_review.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_loop_failure_brake.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_adapter_parity.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_battery.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_fuzz.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_jsonrpc.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_package.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_protocol.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_schemas.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_seen.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_server.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_server_cli.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_mcp_state_tools.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_metabolic.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_migrate.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_model_auto_detect.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_model_offline_default.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_artifact.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_audit.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_audit_integration.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_catalog.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_cli.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_engine_free.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_fingerprint.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_identity.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_importer.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_integration.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_parity.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_paths.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_publish.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_quarantine.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_record.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_record_catalog.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_noosphere_record_publish.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_observatory_engine_free.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_observatory_projection.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_observatory_server.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_observatory_society.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_observatory_static.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_ontological_semantic.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_output_filter.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_output_filter_stages.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_packaging.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_perception.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_perf_reflection.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_plugins.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_quarantine_promote.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_quarantine_trial.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_recursive_loop_integration.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_review_apply.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_risk.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_self_prompt.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_self_prompt_state.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_semantic_dedup.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_semantic_engine.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_semantic_reconcile_integration.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_session_lifecycle.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_session_lifecycle_battery.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_session_lifecycle_dream.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_session_rag.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_shard_engine.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_skills_graft.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_state_loaders_battery.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_state_persistence_battery.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_structural.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_structural_consent.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_structural_drift.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_structural_inject.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_timeutil.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_token_tracker.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_trajectory_vector.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_v2_debt_zero.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_voice_preset.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_workspace.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_world_contradictions.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_world_model_entropy.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_world_model_prediction.py +0 -0
- {conscio-2.6.2 → conscio-2.7.0}/tests/test_world_model_prune.py +0 -0
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [2.7.0] - 2026-06-25 — "Phase 2: General Relay Auto-Respond"
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Awake relay auto-respond (daemon).** A new `--auto-respond` flag lets an
|
|
14
|
+
awake daemon auto-reply to unread free-form relay messages from trusted peers,
|
|
15
|
+
generating one reply each via its LLM adapter and sending it back. OFF by
|
|
16
|
+
default; inert without the `relay` sensor + an adapter + `--awake` +
|
|
17
|
+
`--relay-peer`. `--respond-limit` (default 10) caps adapter calls per cycle.
|
|
18
|
+
- Pure responder `conscio/agency/relay_respond.py` (engine-free; raw adapter
|
|
19
|
+
call, no engine memory). Replies carry `auto_reply: true` + `in_reply_to`.
|
|
20
|
+
|
|
21
|
+
### Notes
|
|
22
|
+
- **1-turn bounded.** A peer's auto-reply (`auto_reply: true`) is consumed but
|
|
23
|
+
never re-answered, so two auto-responders cannot ping-pong. Multi-turn is a
|
|
24
|
+
later phase.
|
|
25
|
+
- **Daemon owns consumption in this mode.** The responder marks handled peer
|
|
26
|
+
rows read (per-row, after each send); the host's `relay_inbox` no longer sees
|
|
27
|
+
them. `RelaySensor` stays read-only.
|
|
28
|
+
- Debt-zero: `conscio/liaison/`, `mcp/server.py`, `mcp/protocol.py`,
|
|
29
|
+
`agency/review_apply.py` unchanged. `relay_send` is a pure mailbox write, not
|
|
30
|
+
`host_act` — the server is untouched.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## [2.6.3] - 2026-06-25 — "Ressalvas"
|
|
35
|
+
|
|
36
|
+
Post-ship hardening of v2.6.2, addressing reviewer ressalvas.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- **Auto-review SQL is throttled.** `--auto-review` previously opened a liaison `SELECT` on every tool call; it now polls at most once per `AUTO_APPLY_THROTTLE_S` (5 s) per `Bindings`. The `host_act` gate remains the authority — this only paces the opportunistic poll.
|
|
40
|
+
- **`RelaySensor` id prefix widened** 8 → 12 chars in observations, to reduce visual id collision in perception/logs.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
- End-to-end integration test wiring a real liaison mailbox + real `HostActChannel`/ledger + the daemon `RelaySensor` + the server `--auto-review` apply, proving the perceive → verdict → `host_act.approve` loop (previously a manual smoke).
|
|
44
|
+
|
|
45
|
+
### Notes
|
|
46
|
+
- **Known limitation (verdict consumption).** `review_apply.apply_verdicts` marks every polled `review_verdict` row read as bound work — including corrupt-payload, non-allowlisted, and stale-fingerprint rows. This cannot affect the normal flow (a `review_request` is published only after the act parks pending, so a peer cannot verdict before the act exists), and a corrected resend is a new mailbox row that polls fresh. A malformed verdict's original row is not re-inspectable.
|
|
47
|
+
- Inbox-flood backoff/stress signaling is deferred — `limit=50` already bounds per-cycle perception work.
|
|
48
|
+
|
|
10
49
|
## [2.6.2] - 2026-06-24 — "Awake Relay Sensor"
|
|
11
50
|
|
|
12
51
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conscio
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
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,10 @@ 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.
|
|
50
|
-
- **Prior:** `v2.6.
|
|
49
|
+
- **Current release:** `v2.7.0` — "Phase 2: General Relay Auto-Respond": an **Awake** daemon can now **auto-reply** to unread free-form relay messages from trusted peers — generating one reply each via its LLM adapter and sending it back, no human in the loop. Behind `--auto-respond` (OFF by default; inert without the `relay` sensor + an adapter + `--awake` + `--relay-peer`), with `--respond-limit` (default 10) capping adapter calls per cycle. The loop is **1-turn bounded**: a peer's auto-reply is consumed but never re-answered, so two auto-responders cannot ping-pong. Replies carry `auto_reply: true` + `in_reply_to`. The new pure responder `conscio/agency/relay_respond.py` is **engine-free** (a thin adapter call, no engine memory) and the MCP server is **untouched** — `relay_send` is a pure mailbox write, not `host_act`. `pip install conscio`.
|
|
50
|
+
- **Prior:** `v2.6.3` — "Ressalvas": post-ship hardening of the Awake Relay loop. `--auto-review` no longer opens a liaison `SELECT` per tool call — the poll is **throttled to once per 5 s** per server (the `host_act` gate stays the authority). The `RelaySensor` id prefix in observations widens 8 → 12 chars to avoid visual collision, and an **end-to-end integration test** now wires a real mailbox + `HostActChannel` + daemon sensor + server auto-apply, proving the perceive → verdict → approve loop in-suite. `pip install conscio`.
|
|
51
|
+
- **Earlier:** `v2.6.2` — "Awake Relay Sensor": an **Awake** instance reacts to peers instead of polling by hand. The daemon gains a read-only `RelaySensor` (`--sensors host,relay --relay-peer <id>`) that reports unread peer messages as a `PerceptionFrame` (`relay_unread`/`review_pending`) each heartbeat and **never marks them read**. The MCP server gains `--auto-review` (off by default): when the proposer is awake and `--enable-act` + `--enable-hermes-review` are on, inbound allowlisted review verdicts are auto-applied to local pending acts on the next tool call — no explicit `conscio.poll_reviews`. The local `host_act` gate stays the authority. **Daemon perceives, server acts**; the shared apply core lives in `conscio/agency/review_apply.py` and `conscio/liaison/*.py` stays engine-free. `pip install conscio`.
|
|
52
|
+
- **Earlier:** `v2.6.1` — "Relay": **general cross-agent messaging** over the same Liaison mailbox. Behind `--enable-relay` (independent of act / hermes-review), `conscio.relay_send`/`relay_inbox`/`relay_read` carry **free-form directed messages** between agents — Claude ↔ Hermes ↔ Antigravity ↔ OpenClaw. `--relay-peer` gates **both** send and receive; the two review types stay **reserved** (the channels never bleed); payloads are capped at 64 KB; read messages are purged after 7 days (unread never deleted). A **dumb pipe** — engine-free, never touches an act; no crypto (shared-filesystem trust domain). Debt-zero: no DB schema change, the review path byte-identical. `pip install conscio`.
|
|
51
53
|
- **Earlier:** `v2.6.0` — "Liaison": cross-agent `hermes_review`. A `hermes_review`-gated act proposed by one agent can be **approved or rejected by a different agent** over a file-mediated shared mailbox (`$HERMES_HOME/liaison.db`, separate from `noosphere.db`). New **engine-free** `conscio/liaison/` (mailbox + pure review protocol); MCP tools behind `--enable-hermes-review` — `conscio.reviews`/`review_approve`/`review_reject` (reviewer role) and `conscio.poll_reviews` (proposer role). A verdict is applied **only** from an allowlisted `--reviewer` and **only** through the unchanged local `host_act` gate; `fp` binds each verdict to one exact proposal (proposer id + ledger id), so cross-instance confusion and replay are impossible. No crypto (shared-filesystem trust domain); directed-only; off by default. `pip install conscio`.
|
|
52
54
|
- **Earlier:** `v2.5.0` — "Society view": the Observatory now also projects the **host-shared noosphere** — the "society" of instances that have published into `noosphere.db`. `conscio-observatory` gains read-only `/api/society/{members,skills,records}` (the census plus published skills/records, **metadata only** — artifact/bundle BLOBs omitted) and Society tabs in the viewer, over the same **engine-free** projection that opens `noosphere.db` with `mode=ro` (no `PRAGMA`, `SELECT` only). `mode=ro` reads the latest committed WAL rows even under a concurrent peer writer (`immutable=1` deliberately rejected). GET-only (mutation → 405); **no `--enable` flag** — launching it is the opt-in. `pip install conscio`.
|
|
53
55
|
- **Earlier:** `v2.4.0` — "Observatory": a **read-only** window into one instance's mind. `conscio-observatory --storage DIR [--token TOK]` serves a **loopback-only** HTTP viewer of the persisted logs, goals, actions, skills, and state — over an **engine-free** projection that opens `conscio.db` with `mode=ro` (no `PRAGMA`, `SELECT` only) and parses `goals.json`/`state_summary.json`. It serves **GET only** (every mutation verb → 405) and reads even a **cold** instance with no engine running. The MCP server also gains read-only `conscio.state`/`conscio.events`/`conscio.handoff` **tools** that delegate to the same helpers its resources use. **No `--enable-observatory` flag** — it has no write/execute surface, so launching it is the opt-in. `pip install conscio`.
|
|
@@ -11,8 +11,10 @@ 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.
|
|
15
|
-
- **Prior:** `v2.6.
|
|
14
|
+
- **Current release:** `v2.7.0` — "Phase 2: General Relay Auto-Respond": an **Awake** daemon can now **auto-reply** to unread free-form relay messages from trusted peers — generating one reply each via its LLM adapter and sending it back, no human in the loop. Behind `--auto-respond` (OFF by default; inert without the `relay` sensor + an adapter + `--awake` + `--relay-peer`), with `--respond-limit` (default 10) capping adapter calls per cycle. The loop is **1-turn bounded**: a peer's auto-reply is consumed but never re-answered, so two auto-responders cannot ping-pong. Replies carry `auto_reply: true` + `in_reply_to`. The new pure responder `conscio/agency/relay_respond.py` is **engine-free** (a thin adapter call, no engine memory) and the MCP server is **untouched** — `relay_send` is a pure mailbox write, not `host_act`. `pip install conscio`.
|
|
15
|
+
- **Prior:** `v2.6.3` — "Ressalvas": post-ship hardening of the Awake Relay loop. `--auto-review` no longer opens a liaison `SELECT` per tool call — the poll is **throttled to once per 5 s** per server (the `host_act` gate stays the authority). The `RelaySensor` id prefix in observations widens 8 → 12 chars to avoid visual collision, and an **end-to-end integration test** now wires a real mailbox + `HostActChannel` + daemon sensor + server auto-apply, proving the perceive → verdict → approve loop in-suite. `pip install conscio`.
|
|
16
|
+
- **Earlier:** `v2.6.2` — "Awake Relay Sensor": an **Awake** instance reacts to peers instead of polling by hand. The daemon gains a read-only `RelaySensor` (`--sensors host,relay --relay-peer <id>`) that reports unread peer messages as a `PerceptionFrame` (`relay_unread`/`review_pending`) each heartbeat and **never marks them read**. The MCP server gains `--auto-review` (off by default): when the proposer is awake and `--enable-act` + `--enable-hermes-review` are on, inbound allowlisted review verdicts are auto-applied to local pending acts on the next tool call — no explicit `conscio.poll_reviews`. The local `host_act` gate stays the authority. **Daemon perceives, server acts**; the shared apply core lives in `conscio/agency/review_apply.py` and `conscio/liaison/*.py` stays engine-free. `pip install conscio`.
|
|
17
|
+
- **Earlier:** `v2.6.1` — "Relay": **general cross-agent messaging** over the same Liaison mailbox. Behind `--enable-relay` (independent of act / hermes-review), `conscio.relay_send`/`relay_inbox`/`relay_read` carry **free-form directed messages** between agents — Claude ↔ Hermes ↔ Antigravity ↔ OpenClaw. `--relay-peer` gates **both** send and receive; the two review types stay **reserved** (the channels never bleed); payloads are capped at 64 KB; read messages are purged after 7 days (unread never deleted). A **dumb pipe** — engine-free, never touches an act; no crypto (shared-filesystem trust domain). Debt-zero: no DB schema change, the review path byte-identical. `pip install conscio`.
|
|
16
18
|
- **Earlier:** `v2.6.0` — "Liaison": cross-agent `hermes_review`. A `hermes_review`-gated act proposed by one agent can be **approved or rejected by a different agent** over a file-mediated shared mailbox (`$HERMES_HOME/liaison.db`, separate from `noosphere.db`). New **engine-free** `conscio/liaison/` (mailbox + pure review protocol); MCP tools behind `--enable-hermes-review` — `conscio.reviews`/`review_approve`/`review_reject` (reviewer role) and `conscio.poll_reviews` (proposer role). A verdict is applied **only** from an allowlisted `--reviewer` and **only** through the unchanged local `host_act` gate; `fp` binds each verdict to one exact proposal (proposer id + ledger id), so cross-instance confusion and replay are impossible. No crypto (shared-filesystem trust domain); directed-only; off by default. `pip install conscio`.
|
|
17
19
|
- **Earlier:** `v2.5.0` — "Society view": the Observatory now also projects the **host-shared noosphere** — the "society" of instances that have published into `noosphere.db`. `conscio-observatory` gains read-only `/api/society/{members,skills,records}` (the census plus published skills/records, **metadata only** — artifact/bundle BLOBs omitted) and Society tabs in the viewer, over the same **engine-free** projection that opens `noosphere.db` with `mode=ro` (no `PRAGMA`, `SELECT` only). `mode=ro` reads the latest committed WAL rows even under a concurrent peer writer (`immutable=1` deliberately rejected). GET-only (mutation → 405); **no `--enable` flag** — launching it is the opt-in. `pip install conscio`.
|
|
18
20
|
- **Earlier:** `v2.4.0` — "Observatory": a **read-only** window into one instance's mind. `conscio-observatory --storage DIR [--token TOK]` serves a **loopback-only** HTTP viewer of the persisted logs, goals, actions, skills, and state — over an **engine-free** projection that opens `conscio.db` with `mode=ro` (no `PRAGMA`, `SELECT` only) and parses `goals.json`/`state_summary.json`. It serves **GET only** (every mutation verb → 405) and reads even a **cold** instance with no engine running. The MCP server also gains read-only `conscio.state`/`conscio.events`/`conscio.handoff` **tools** that delegate to the same helpers its resources use. **No `--enable-observatory` flag** — it has no write/execute surface, so launching it is the opt-in. `pip install conscio`.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# conscio/agency/relay_respond.py
|
|
2
|
+
"""Pure relay auto-responder — the daemon's awake auto-reply path (v2.7.0
|
|
3
|
+
"Phase 2"). Reads unread free-form relay messages from allowlisted peers,
|
|
4
|
+
generates one reply each via a raw InferenceAdapter, and sends it back over the
|
|
5
|
+
mailbox. An agency->liaison bridge: imports nothing from conscio.engine, so the
|
|
6
|
+
reply is a thin adapter call (no engine memory/advisory). Loop-bounded: own
|
|
7
|
+
replies carry payload.auto_reply=True and such inbound is consumed, never
|
|
8
|
+
re-answered. mark_read is per-row, immediately after a successful send / skip."""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
|
|
13
|
+
from ..liaison import mailbox, relay
|
|
14
|
+
|
|
15
|
+
DEFAULT_SYSTEM = (
|
|
16
|
+
"You are an autonomous agent replying to a peer agent over a relay "
|
|
17
|
+
"channel. Read the peer's message and reply concisely and helpfully. "
|
|
18
|
+
"Reply with prose only.")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _msg_text(payload) -> str:
|
|
22
|
+
"""Best-effort human text from a relay payload; falls back to compact JSON."""
|
|
23
|
+
if isinstance(payload, dict):
|
|
24
|
+
for k in ("text", "body", "message"):
|
|
25
|
+
v = payload.get(k)
|
|
26
|
+
if isinstance(v, str) and v:
|
|
27
|
+
return v
|
|
28
|
+
return json.dumps(payload, separators=(",", ":"))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _fit(reply: dict) -> dict:
|
|
32
|
+
"""Shrink reply['text'] until the compact-JSON payload fits the relay cap.
|
|
33
|
+
UTF-8-safe (slices code points, re-encodes via payload_size)."""
|
|
34
|
+
if relay.payload_size(reply) <= relay.MAX_PAYLOAD_BYTES:
|
|
35
|
+
return reply
|
|
36
|
+
text = reply["text"]
|
|
37
|
+
lo, hi = 0, len(text)
|
|
38
|
+
while lo < hi: # largest prefix that fits
|
|
39
|
+
mid = (lo + hi + 1) // 2
|
|
40
|
+
if relay.payload_size(dict(reply, text=text[:mid])) <= \
|
|
41
|
+
relay.MAX_PAYLOAD_BYTES:
|
|
42
|
+
lo = mid
|
|
43
|
+
else:
|
|
44
|
+
hi = mid - 1
|
|
45
|
+
return dict(reply, text=text[:lo])
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def auto_respond(adapter, liaison_db, self_id, peers, *, limit: int = 10,
|
|
49
|
+
max_reply_tokens: int = 512, system: str = DEFAULT_SYSTEM
|
|
50
|
+
) -> list[dict]:
|
|
51
|
+
"""Auto-reply to unread peer relay messages. Returns sent packets.
|
|
52
|
+
No-op ([]) when adapter is None, liaison_db/self_id falsy, or peers empty."""
|
|
53
|
+
if adapter is None or not liaison_db or not self_id or not peers:
|
|
54
|
+
return []
|
|
55
|
+
peers = set(peers)
|
|
56
|
+
rows = mailbox.inbox(liaison_db, self_id, types=None,
|
|
57
|
+
unread_only=True, limit=limit) # newest-first, capped
|
|
58
|
+
sent: list[dict] = []
|
|
59
|
+
for m in rows:
|
|
60
|
+
typ = m.get("type", "")
|
|
61
|
+
frm = m.get("from_instance", "")
|
|
62
|
+
payload = m.get("payload")
|
|
63
|
+
if typ in relay.RESERVED_TYPES:
|
|
64
|
+
continue # review channel owns it
|
|
65
|
+
if not relay.is_relay_message(m, peers):
|
|
66
|
+
continue # non-peer/oversized: not ours
|
|
67
|
+
if isinstance(payload, dict) and payload.get("auto_reply") is True:
|
|
68
|
+
mailbox.mark_read(liaison_db, [m["id"]]) # R2: consume, don't answer
|
|
69
|
+
continue
|
|
70
|
+
prompt = system + "\n\nPeer message:\n" + _msg_text(payload)
|
|
71
|
+
try:
|
|
72
|
+
text = adapter.generate(prompt, max_tokens=max_reply_tokens).text
|
|
73
|
+
except Exception: # fork 4: leave UNREAD, retry
|
|
74
|
+
continue
|
|
75
|
+
reply = _fit({"text": text, "auto_reply": True,
|
|
76
|
+
"in_reply_to": m["id"]})
|
|
77
|
+
rid = mailbox.send(liaison_db, from_instance=self_id, to_instance=frm,
|
|
78
|
+
type=typ, payload=reply) # type echoed
|
|
79
|
+
mailbox.mark_read(liaison_db, [m["id"]]) # R2: per-row, post-send
|
|
80
|
+
sent.append({"to": frm, "in_reply_to": m["id"], "reply_id": rid})
|
|
81
|
+
return sent
|
|
@@ -56,6 +56,7 @@ class Daemon:
|
|
|
56
56
|
def __init__(self, engine: Any, sensors: Sequence[SensorAdapter], *,
|
|
57
57
|
interval: float = 5.0, budget: Optional[ActBudget] = None,
|
|
58
58
|
on_cycle: Optional[Callable[[list, RunReport], Any]] = None,
|
|
59
|
+
responder: Optional[Callable[[], list]] = None,
|
|
59
60
|
workspace: Any = None,
|
|
60
61
|
consent: Any = None,
|
|
61
62
|
pidfile: Optional[str | Path] = None,
|
|
@@ -66,6 +67,7 @@ class Daemon:
|
|
|
66
67
|
self.interval = interval
|
|
67
68
|
self.budget = budget
|
|
68
69
|
self.on_cycle = on_cycle or (lambda frames, result: None)
|
|
70
|
+
self.responder = responder # v2.7.0: relay auto-reply
|
|
69
71
|
self.workspace = workspace
|
|
70
72
|
self.consent = consent # v1.7.2: structural consent
|
|
71
73
|
self._synced_ws_id: Optional[str] = None # re-sync only on ws change
|
|
@@ -104,6 +106,11 @@ class Daemon:
|
|
|
104
106
|
result = self.engine.run(self.budget, world_state=world_state)
|
|
105
107
|
self.cycles += 1
|
|
106
108
|
self._last_report = result
|
|
109
|
+
if self.responder is not None: # v2.7.0: awake auto-reply
|
|
110
|
+
try:
|
|
111
|
+
self.responder()
|
|
112
|
+
except Exception as exc: # a bad responder never kills the loop
|
|
113
|
+
log.warning("relay responder failed: %s", exc)
|
|
107
114
|
# Write heartbeat every cycle (not just on shutdown)
|
|
108
115
|
self._write_heartbeat()
|
|
109
116
|
try:
|
|
@@ -248,6 +255,14 @@ def _build_sensors(spec: str, *, agent_source: Optional[str],
|
|
|
248
255
|
return sensors
|
|
249
256
|
|
|
250
257
|
|
|
258
|
+
def _responder_armed(*, auto_respond: bool, relay_peer, has_adapter: bool,
|
|
259
|
+
awake: bool, sensors_spec: str) -> bool:
|
|
260
|
+
"""True iff --auto-respond should arm: needs a relay sensor + an adapter +
|
|
261
|
+
--awake + at least one --relay-peer (v2.7.0)."""
|
|
262
|
+
return bool(auto_respond and relay_peer and has_adapter and awake
|
|
263
|
+
and "relay" in [s.strip() for s in sensors_spec.split(",")])
|
|
264
|
+
|
|
265
|
+
|
|
251
266
|
# ── config loader + adapter builder live in conscio/adapter_config.py now
|
|
252
267
|
# (shared by the daemon and the MCP server; v2.0.1, no behavior change) ──
|
|
253
268
|
|
|
@@ -312,6 +327,12 @@ def _arg_parser() -> argparse.ArgumentParser:
|
|
|
312
327
|
parser.add_argument("--relay-peer", action="append", default=[],
|
|
313
328
|
metavar="INSTANCE_ID",
|
|
314
329
|
help="trusted relay peer for the relay sensor (repeatable)")
|
|
330
|
+
parser.add_argument("--auto-respond", action="store_true",
|
|
331
|
+
help="daemon auto-replies to unread relay peer messages "
|
|
332
|
+
"via the adapter (OFF default; needs relay sensor + "
|
|
333
|
+
"adapter + --awake + --relay-peer)")
|
|
334
|
+
parser.add_argument("--respond-limit", type=int, default=10,
|
|
335
|
+
help="max relay auto-replies per cycle (token-burn cap)")
|
|
315
336
|
return parser
|
|
316
337
|
|
|
317
338
|
|
|
@@ -370,8 +391,25 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
370
391
|
if args.budget_cycles else None)
|
|
371
392
|
workspace = WorkspaceContext(emit=engine.event_bus.emit)
|
|
372
393
|
consent = StructuralConsent(consent_path(engine.storage)) # v1.7.2
|
|
394
|
+
responder = None # v2.7.0
|
|
395
|
+
if args.auto_respond:
|
|
396
|
+
if _responder_armed(auto_respond=True, relay_peer=args.relay_peer,
|
|
397
|
+
has_adapter=adapter is not None, awake=awake,
|
|
398
|
+
sensors_spec=sensors_spec):
|
|
399
|
+
from .agency import relay_respond
|
|
400
|
+
_adapter = adapter # R1: bind LOCAL adapter
|
|
401
|
+
_peers = tuple(args.relay_peer)
|
|
402
|
+
responder = lambda: relay_respond.auto_respond( # noqa: E731
|
|
403
|
+
_adapter, liaison_db, self_id, _peers,
|
|
404
|
+
limit=args.respond_limit)
|
|
405
|
+
log.info("relay auto-respond armed (peers=%d, limit=%d)",
|
|
406
|
+
len(_peers), args.respond_limit)
|
|
407
|
+
else:
|
|
408
|
+
log.warning("--auto-respond inert: needs relay sensor + adapter + "
|
|
409
|
+
"--awake + --relay-peer; skipping")
|
|
373
410
|
daemon = Daemon(engine, sensors=sensors, interval=interval,
|
|
374
|
-
budget=budget, workspace=workspace, consent=consent
|
|
411
|
+
budget=budget, workspace=workspace, consent=consent,
|
|
412
|
+
responder=responder)
|
|
375
413
|
daemon.run(once=args.once)
|
|
376
414
|
return 0
|
|
377
415
|
|
|
@@ -24,6 +24,11 @@ from .seen import SeenStore
|
|
|
24
24
|
from ..agency import review_apply
|
|
25
25
|
from ..liaison import mailbox, relay, review
|
|
26
26
|
|
|
27
|
+
# v2.6.3 #2: floor between auto-review SQL polls so --auto-review does not open a
|
|
28
|
+
# liaison SELECT on every single tool call in a chatty session. host_act.approve
|
|
29
|
+
# remains the authority; this only paces the opportunistic poll.
|
|
30
|
+
AUTO_APPLY_THROTTLE_S = 5.0
|
|
31
|
+
|
|
27
32
|
|
|
28
33
|
class Bindings:
|
|
29
34
|
def __init__(self, engine: ConsciousnessEngine, seen: SeenStore, *,
|
|
@@ -50,6 +55,7 @@ class Bindings:
|
|
|
50
55
|
self.relay = relay # v2.6.1: --enable-relay
|
|
51
56
|
self.relay_peers = tuple(relay_peers)
|
|
52
57
|
self.auto_review = auto_review # v2.6.2: --auto-review
|
|
58
|
+
self.last_auto_apply_ts = 0.0 # v2.6.3 #2: throttle clock
|
|
53
59
|
|
|
54
60
|
# ── discovery ──
|
|
55
61
|
def version(self) -> str:
|
|
@@ -262,6 +268,10 @@ class Bindings:
|
|
|
262
268
|
return
|
|
263
269
|
if self.engine.host_act is None or not self.reviewers:
|
|
264
270
|
return
|
|
271
|
+
now = time.monotonic() # v2.6.3 #2: pace the SQL poll
|
|
272
|
+
if now - self.last_auto_apply_ts < AUTO_APPLY_THROTTLE_S:
|
|
273
|
+
return
|
|
274
|
+
self.last_auto_apply_ts = now
|
|
265
275
|
try:
|
|
266
276
|
review_apply.apply_verdicts(self.engine.host_act, self.liaison_db,
|
|
267
277
|
self.self_instance_id, self.reviewers)
|
|
@@ -13,6 +13,8 @@ from ..liaison.relay import RESERVED_TYPES
|
|
|
13
13
|
from ..risk import Risk
|
|
14
14
|
from .sensor import PerceptionFrame, SensorAdapter
|
|
15
15
|
|
|
16
|
+
_ID_DISPLAY = 12 # v2.6.3 #4: wider id prefix; 8 risked visual collision
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
class RelaySensor(SensorAdapter):
|
|
18
20
|
"""Unread liaison inbox as a `PerceptionFrame` (source ``"relay"``)."""
|
|
@@ -46,9 +48,9 @@ class RelaySensor(SensorAdapter):
|
|
|
46
48
|
else:
|
|
47
49
|
other_n += 1 # non-peer: counted, not detailed
|
|
48
50
|
for frm, n in sorted(relay_from.items()):
|
|
49
|
-
obs.append(f"relay: {n} unread from {frm[:
|
|
51
|
+
obs.append(f"relay: {n} unread from {frm[:_ID_DISPLAY]}")
|
|
50
52
|
for frm, n in sorted(review_from.items()):
|
|
51
|
-
obs.append(f"review: {n} verdict(s) pending from {frm[:
|
|
53
|
+
obs.append(f"review: {n} verdict(s) pending from {frm[:_ID_DISPLAY]}")
|
|
52
54
|
if other_n:
|
|
53
55
|
obs.append(f"relay: {other_n} unread from non-peers (ignored)")
|
|
54
56
|
if not obs:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conscio
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
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,10 @@ 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.
|
|
50
|
-
- **Prior:** `v2.6.
|
|
49
|
+
- **Current release:** `v2.7.0` — "Phase 2: General Relay Auto-Respond": an **Awake** daemon can now **auto-reply** to unread free-form relay messages from trusted peers — generating one reply each via its LLM adapter and sending it back, no human in the loop. Behind `--auto-respond` (OFF by default; inert without the `relay` sensor + an adapter + `--awake` + `--relay-peer`), with `--respond-limit` (default 10) capping adapter calls per cycle. The loop is **1-turn bounded**: a peer's auto-reply is consumed but never re-answered, so two auto-responders cannot ping-pong. Replies carry `auto_reply: true` + `in_reply_to`. The new pure responder `conscio/agency/relay_respond.py` is **engine-free** (a thin adapter call, no engine memory) and the MCP server is **untouched** — `relay_send` is a pure mailbox write, not `host_act`. `pip install conscio`.
|
|
50
|
+
- **Prior:** `v2.6.3` — "Ressalvas": post-ship hardening of the Awake Relay loop. `--auto-review` no longer opens a liaison `SELECT` per tool call — the poll is **throttled to once per 5 s** per server (the `host_act` gate stays the authority). The `RelaySensor` id prefix in observations widens 8 → 12 chars to avoid visual collision, and an **end-to-end integration test** now wires a real mailbox + `HostActChannel` + daemon sensor + server auto-apply, proving the perceive → verdict → approve loop in-suite. `pip install conscio`.
|
|
51
|
+
- **Earlier:** `v2.6.2` — "Awake Relay Sensor": an **Awake** instance reacts to peers instead of polling by hand. The daemon gains a read-only `RelaySensor` (`--sensors host,relay --relay-peer <id>`) that reports unread peer messages as a `PerceptionFrame` (`relay_unread`/`review_pending`) each heartbeat and **never marks them read**. The MCP server gains `--auto-review` (off by default): when the proposer is awake and `--enable-act` + `--enable-hermes-review` are on, inbound allowlisted review verdicts are auto-applied to local pending acts on the next tool call — no explicit `conscio.poll_reviews`. The local `host_act` gate stays the authority. **Daemon perceives, server acts**; the shared apply core lives in `conscio/agency/review_apply.py` and `conscio/liaison/*.py` stays engine-free. `pip install conscio`.
|
|
52
|
+
- **Earlier:** `v2.6.1` — "Relay": **general cross-agent messaging** over the same Liaison mailbox. Behind `--enable-relay` (independent of act / hermes-review), `conscio.relay_send`/`relay_inbox`/`relay_read` carry **free-form directed messages** between agents — Claude ↔ Hermes ↔ Antigravity ↔ OpenClaw. `--relay-peer` gates **both** send and receive; the two review types stay **reserved** (the channels never bleed); payloads are capped at 64 KB; read messages are purged after 7 days (unread never deleted). A **dumb pipe** — engine-free, never touches an act; no crypto (shared-filesystem trust domain). Debt-zero: no DB schema change, the review path byte-identical. `pip install conscio`.
|
|
51
53
|
- **Earlier:** `v2.6.0` — "Liaison": cross-agent `hermes_review`. A `hermes_review`-gated act proposed by one agent can be **approved or rejected by a different agent** over a file-mediated shared mailbox (`$HERMES_HOME/liaison.db`, separate from `noosphere.db`). New **engine-free** `conscio/liaison/` (mailbox + pure review protocol); MCP tools behind `--enable-hermes-review` — `conscio.reviews`/`review_approve`/`review_reject` (reviewer role) and `conscio.poll_reviews` (proposer role). A verdict is applied **only** from an allowlisted `--reviewer` and **only** through the unchanged local `host_act` gate; `fp` binds each verdict to one exact proposal (proposer id + ledger id), so cross-instance confusion and replay are impossible. No crypto (shared-filesystem trust domain); directed-only; off by default. `pip install conscio`.
|
|
52
54
|
- **Earlier:** `v2.5.0` — "Society view": the Observatory now also projects the **host-shared noosphere** — the "society" of instances that have published into `noosphere.db`. `conscio-observatory` gains read-only `/api/society/{members,skills,records}` (the census plus published skills/records, **metadata only** — artifact/bundle BLOBs omitted) and Society tabs in the viewer, over the same **engine-free** projection that opens `noosphere.db` with `mode=ro` (no `PRAGMA`, `SELECT` only). `mode=ro` reads the latest committed WAL rows even under a concurrent peer writer (`immutable=1` deliberately rejected). GET-only (mutation → 405); **no `--enable` flag** — launching it is the opt-in. `pip install conscio`.
|
|
53
55
|
- **Earlier:** `v2.4.0` — "Observatory": a **read-only** window into one instance's mind. `conscio-observatory --storage DIR [--token TOK]` serves a **loopback-only** HTTP viewer of the persisted logs, goals, actions, skills, and state — over an **engine-free** projection that opens `conscio.db` with `mode=ro` (no `PRAGMA`, `SELECT` only) and parses `goals.json`/`state_summary.json`. It serves **GET only** (every mutation verb → 405) and reads even a **cold** instance with no engine running. The MCP server also gains read-only `conscio.state`/`conscio.events`/`conscio.handoff` **tools** that delegate to the same helpers its resources use. **No `--enable-observatory` flag** — it has no write/execute surface, so launching it is the opt-in. `pip install conscio`.
|
|
@@ -64,6 +64,7 @@ conscio/agency/ledger.py
|
|
|
64
64
|
conscio/agency/loop.py
|
|
65
65
|
conscio/agency/profiles.py
|
|
66
66
|
conscio/agency/promote.py
|
|
67
|
+
conscio/agency/relay_respond.py
|
|
67
68
|
conscio/agency/review_apply.py
|
|
68
69
|
conscio/agency/skeptic.py
|
|
69
70
|
conscio/agency/skills.py
|
|
@@ -168,6 +169,7 @@ tests/test_daemon_battery.py
|
|
|
168
169
|
tests/test_daemon_config.py
|
|
169
170
|
tests/test_daemon_heartbeat.py
|
|
170
171
|
tests/test_daemon_relay.py
|
|
172
|
+
tests/test_daemon_responder.py
|
|
171
173
|
tests/test_daemon_structure_sync.py
|
|
172
174
|
tests/test_dream_coherence.py
|
|
173
175
|
tests/test_dream_distill.py
|
|
@@ -249,6 +251,8 @@ tests/test_plugins.py
|
|
|
249
251
|
tests/test_quarantine_promote.py
|
|
250
252
|
tests/test_quarantine_trial.py
|
|
251
253
|
tests/test_recursive_loop_integration.py
|
|
254
|
+
tests/test_relay_e2e.py
|
|
255
|
+
tests/test_relay_respond.py
|
|
252
256
|
tests/test_relay_sensor.py
|
|
253
257
|
tests/test_review_apply.py
|
|
254
258
|
tests/test_risk.py
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# tests/test_daemon_responder.py
|
|
2
|
+
"""v2.7.0 Phase 2 — daemon responder hook + arm gate."""
|
|
3
|
+
from conscio.daemon import Daemon, _responder_armed
|
|
4
|
+
from conscio.engine import ConsciousnessEngine
|
|
5
|
+
from conscio.perception import MockSensor, PerceptionFrame
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _engine(tmp_path):
|
|
9
|
+
return ConsciousnessEngine("glm-5.1", storage_path=tmp_path / "s")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _frame():
|
|
13
|
+
return PerceptionFrame(source="mock", observations=["o"], signals={"x": 1.0})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_responder_called_after_run(tmp_path):
|
|
17
|
+
eng = _engine(tmp_path)
|
|
18
|
+
calls = {"n": 0}
|
|
19
|
+
d = Daemon(eng, sensors=[MockSensor([_frame()])],
|
|
20
|
+
responder=lambda: calls.__setitem__("n", calls["n"] + 1))
|
|
21
|
+
try:
|
|
22
|
+
d.cycle()
|
|
23
|
+
assert calls["n"] == 1
|
|
24
|
+
finally:
|
|
25
|
+
eng.close()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_responder_raise_survives_cycle(tmp_path):
|
|
29
|
+
eng = _engine(tmp_path)
|
|
30
|
+
|
|
31
|
+
def boom():
|
|
32
|
+
raise RuntimeError("responder down")
|
|
33
|
+
|
|
34
|
+
d = Daemon(eng, sensors=[MockSensor([_frame()])], responder=boom)
|
|
35
|
+
try:
|
|
36
|
+
d.cycle() # must not raise
|
|
37
|
+
finally:
|
|
38
|
+
eng.close()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_responder_none_is_noop(tmp_path):
|
|
42
|
+
eng = _engine(tmp_path)
|
|
43
|
+
d = Daemon(eng, sensors=[MockSensor([_frame()])]) # no responder
|
|
44
|
+
try:
|
|
45
|
+
d.cycle() # no raise
|
|
46
|
+
finally:
|
|
47
|
+
eng.close()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_arm_gate_predicate():
|
|
51
|
+
base = dict(auto_respond=True, relay_peer=["p"], has_adapter=True,
|
|
52
|
+
awake=True, sensors_spec="host,relay")
|
|
53
|
+
assert _responder_armed(**base) is True
|
|
54
|
+
assert _responder_armed(**{**base, "auto_respond": False}) is False
|
|
55
|
+
assert _responder_armed(**{**base, "relay_peer": []}) is False
|
|
56
|
+
assert _responder_armed(**{**base, "has_adapter": False}) is False
|
|
57
|
+
assert _responder_armed(**{**base, "awake": False}) is False
|
|
58
|
+
assert _responder_armed(**{**base, "sensors_spec": "host"}) is False
|
|
@@ -88,3 +88,28 @@ def test_call_tool_triggers_apply():
|
|
|
88
88
|
b = _bind(awake=True, host_act=ha, reviewers=("r",), auto_review=True)
|
|
89
89
|
b.call_tool("conscio.advisory", {}) # any tool; advisory needs no host
|
|
90
90
|
assert ha.calls == 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ── v2.6.3 #2: timestamp throttle (no SQL on every request) ──
|
|
94
|
+
|
|
95
|
+
def test_throttle_skips_within_window(monkeypatch):
|
|
96
|
+
import conscio.mcp.server as srv
|
|
97
|
+
clock = [1000.0]
|
|
98
|
+
monkeypatch.setattr(srv.time, "monotonic", lambda: clock[0])
|
|
99
|
+
ha = _HA()
|
|
100
|
+
b = _bind(awake=True, host_act=ha, reviewers=("r",), auto_review=True)
|
|
101
|
+
b._maybe_auto_apply()
|
|
102
|
+
b._maybe_auto_apply() # same instant -> throttled, no SQL
|
|
103
|
+
assert ha.calls == 1
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_throttle_allows_after_window(monkeypatch):
|
|
107
|
+
import conscio.mcp.server as srv
|
|
108
|
+
clock = [1000.0]
|
|
109
|
+
monkeypatch.setattr(srv.time, "monotonic", lambda: clock[0])
|
|
110
|
+
ha = _HA()
|
|
111
|
+
b = _bind(awake=True, host_act=ha, reviewers=("r",), auto_review=True)
|
|
112
|
+
b._maybe_auto_apply()
|
|
113
|
+
clock[0] += srv.AUTO_APPLY_THROTTLE_S + 0.1
|
|
114
|
+
b._maybe_auto_apply() # window elapsed -> applies again
|
|
115
|
+
assert ha.calls == 2
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# tests/test_relay_e2e.py
|
|
2
|
+
"""v2.6.3 #5: end-to-end integration of the v2.6.2 Awake Relay loop wired
|
|
3
|
+
against REAL parts simultaneously — real liaison mailbox, real HostActChannel +
|
|
4
|
+
ledger, the daemon-side RelaySensor (perception, read-only), and the
|
|
5
|
+
server-side Bindings auto-apply (--auto-review). Only the Skeptic/Breaker/Trust
|
|
6
|
+
and the engine shell are faked; the perceive -> verdict -> approve path is real.
|
|
7
|
+
Converts the previously-manual "full loop" claim into a regression guard."""
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from conscio.agency.contracts import AuditVerdict
|
|
11
|
+
from conscio.agency.host_act import HostActChannel
|
|
12
|
+
from conscio.agency.ledger import ActionLedger
|
|
13
|
+
from conscio.agency.tools import registry_from_manifest
|
|
14
|
+
from conscio.liaison import mailbox, review
|
|
15
|
+
from conscio.mcp.server import Bindings
|
|
16
|
+
from conscio.perception.relay_sensor import RelaySensor
|
|
17
|
+
|
|
18
|
+
SELF = "me-instance-0001"
|
|
19
|
+
REVIEWER = "hermet-instance-0002"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _PassSkeptic:
|
|
23
|
+
def audit(self, proposal, *, goal_text=""):
|
|
24
|
+
return AuditVerdict(verdict="PASS", reasons=[], risk_flags=[],
|
|
25
|
+
confidence=0.9)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _NoBreaker:
|
|
29
|
+
def global_lockdown_due(self): return False
|
|
30
|
+
def should_trip(self, goal_fp, *, task_type=""): return False
|
|
31
|
+
def trip(self, goal_fp, *, detail="", goal_text=""): pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _Trust:
|
|
35
|
+
def on_success(self, tool): pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class _Seen:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _Engine:
|
|
43
|
+
"""Minimal engine carrier: real host_act behind the surface Bindings reads."""
|
|
44
|
+
def __init__(self, host_act):
|
|
45
|
+
self.awake = True
|
|
46
|
+
self.host_act = host_act
|
|
47
|
+
|
|
48
|
+
def advisory(self):
|
|
49
|
+
return {"ok": True}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _host_act(tmp_path):
|
|
53
|
+
led = ActionLedger(tmp_path / "conscio.db")
|
|
54
|
+
manifest = [{"name": "deploy",
|
|
55
|
+
"params": {"env": {"type": "str", "required": True}},
|
|
56
|
+
"risk": "low", "approval_policy": "require_approval"}]
|
|
57
|
+
chan = HostActChannel(
|
|
58
|
+
ledger=led, skeptic=_PassSkeptic(), breaker=_NoBreaker(), trust=_Trust(),
|
|
59
|
+
registry=registry_from_manifest(manifest),
|
|
60
|
+
emit_fn=lambda **kw: None, awake_fn=lambda: True)
|
|
61
|
+
return chan, led
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _bindings(engine, db):
|
|
65
|
+
return Bindings(engine, _Seen(), auto_review=True, hermes_review=True,
|
|
66
|
+
reviewers=(REVIEWER,), self_instance_id=SELF, liaison_db=db)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_full_loop_perceive_then_auto_apply(tmp_path):
|
|
70
|
+
db = tmp_path / "liaison.db"
|
|
71
|
+
chan, led = _host_act(tmp_path)
|
|
72
|
+
|
|
73
|
+
# 1. propose a require_approval act -> parks pending in the real ledger.
|
|
74
|
+
out = chan.propose({"tool": "deploy", "args": {"env": "prod"},
|
|
75
|
+
"rationale": "ship", "expected_outcome": "live"})
|
|
76
|
+
assert out["status"] == "pending_approval"
|
|
77
|
+
rid = out["ledger_id"]
|
|
78
|
+
assert led.get(rid)["status"] == "proposed"
|
|
79
|
+
|
|
80
|
+
# 2. an allowlisted reviewer sends an approve verdict for THIS act's fp.
|
|
81
|
+
row = chan.pending(10)[0]
|
|
82
|
+
fp = review.fingerprint(SELF, row["goal_fp"], row["tool"],
|
|
83
|
+
json.loads(row["args_json"]), row["id"])
|
|
84
|
+
mailbox.send(db, from_instance=REVIEWER, to_instance=SELF,
|
|
85
|
+
type="review_verdict",
|
|
86
|
+
payload=review.build_verdict(fp=fp, decision="approve",
|
|
87
|
+
reason=""))
|
|
88
|
+
|
|
89
|
+
# 3. DAEMON side: RelaySensor perceives the pending verdict, read-only.
|
|
90
|
+
frame = RelaySensor(db, SELF, [REVIEWER]).perceive()
|
|
91
|
+
assert frame.signals["review_pending"] == 1.0
|
|
92
|
+
assert mailbox.inbox(db, SELF, types=["review_verdict"],
|
|
93
|
+
unread_only=True) # sensor did NOT consume
|
|
94
|
+
|
|
95
|
+
# 4. SERVER side: a plain tool call (NOT poll_reviews) auto-applies it.
|
|
96
|
+
eng = _Engine(chan)
|
|
97
|
+
b = _bindings(eng, db)
|
|
98
|
+
b.call_tool("conscio.advisory", {})
|
|
99
|
+
|
|
100
|
+
# 5. the act is approved by the host_act gate, the verdict is consumed.
|
|
101
|
+
assert led.get(rid)["status"] == "executing"
|
|
102
|
+
assert mailbox.inbox(db, SELF, types=["review_verdict"],
|
|
103
|
+
unread_only=True) == []
|
|
104
|
+
led.close()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_non_peer_verdict_does_not_apply(tmp_path):
|
|
108
|
+
"""A verdict from a non-allowlisted sender never moves the act."""
|
|
109
|
+
db = tmp_path / "liaison.db"
|
|
110
|
+
chan, led = _host_act(tmp_path)
|
|
111
|
+
out = chan.propose({"tool": "deploy", "args": {"env": "prod"},
|
|
112
|
+
"rationale": "ship", "expected_outcome": "live"})
|
|
113
|
+
rid = out["ledger_id"]
|
|
114
|
+
row = chan.pending(10)[0]
|
|
115
|
+
fp = review.fingerprint(SELF, row["goal_fp"], row["tool"],
|
|
116
|
+
json.loads(row["args_json"]), row["id"])
|
|
117
|
+
mailbox.send(db, from_instance="stranger-9999", to_instance=SELF,
|
|
118
|
+
type="review_verdict",
|
|
119
|
+
payload=review.build_verdict(fp=fp, decision="approve",
|
|
120
|
+
reason=""))
|
|
121
|
+
b = _bindings(_Engine(chan), db)
|
|
122
|
+
b.call_tool("conscio.advisory", {})
|
|
123
|
+
assert led.get(rid)["status"] == "proposed" # untouched
|
|
124
|
+
led.close()
|