conscio 2.2.2__tar.gz → 2.4.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.
Files changed (266) hide show
  1. {conscio-2.2.2 → conscio-2.4.0}/CHANGELOG.md +49 -0
  2. {conscio-2.2.2/conscio.egg-info → conscio-2.4.0}/PKG-INFO +5 -3
  3. {conscio-2.2.2 → conscio-2.4.0}/README.md +4 -2
  4. {conscio-2.2.2 → conscio-2.4.0}/conscio/__init__.py +1 -1
  5. conscio-2.4.0/conscio/agency/promote.py +55 -0
  6. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/skills.py +20 -0
  7. {conscio-2.2.2 → conscio-2.4.0}/conscio/cli.py +45 -0
  8. {conscio-2.2.2 → conscio-2.4.0}/conscio/engine.py +73 -0
  9. {conscio-2.2.2 → conscio-2.4.0}/conscio/mcp/schemas.py +13 -0
  10. {conscio-2.2.2 → conscio-2.4.0}/conscio/mcp/server.py +26 -14
  11. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/quarantine.py +30 -4
  12. conscio-2.4.0/conscio/observatory/__init__.py +2 -0
  13. conscio-2.4.0/conscio/observatory/projection.py +97 -0
  14. conscio-2.4.0/conscio/observatory/server.py +193 -0
  15. conscio-2.4.0/conscio/observatory/static/app.js +26 -0
  16. conscio-2.4.0/conscio/observatory/static/index.html +25 -0
  17. conscio-2.4.0/conscio/observatory/static/style.css +10 -0
  18. {conscio-2.2.2 → conscio-2.4.0/conscio.egg-info}/PKG-INFO +5 -3
  19. {conscio-2.2.2 → conscio-2.4.0}/conscio.egg-info/SOURCES.txt +17 -0
  20. {conscio-2.2.2 → conscio-2.4.0}/conscio.egg-info/entry_points.txt +1 -0
  21. {conscio-2.2.2 → conscio-2.4.0}/pyproject.toml +2 -1
  22. conscio-2.4.0/tests/test_agency_promote.py +56 -0
  23. conscio-2.4.0/tests/test_cli_promote.py +45 -0
  24. conscio-2.4.0/tests/test_engine_promote.py +194 -0
  25. conscio-2.4.0/tests/test_mcp_state_tools.py +54 -0
  26. conscio-2.4.0/tests/test_observatory_engine_free.py +40 -0
  27. conscio-2.4.0/tests/test_observatory_projection.py +99 -0
  28. conscio-2.4.0/tests/test_observatory_server.py +61 -0
  29. conscio-2.4.0/tests/test_observatory_static.py +22 -0
  30. conscio-2.4.0/tests/test_quarantine_promote.py +66 -0
  31. conscio-2.4.0/tests/test_skills_graft.py +55 -0
  32. {conscio-2.2.2 → conscio-2.4.0}/LICENSE +0 -0
  33. {conscio-2.2.2 → conscio-2.4.0}/MANIFEST.in +0 -0
  34. {conscio-2.2.2 → conscio-2.4.0}/conscio/__main__.py +0 -0
  35. {conscio-2.2.2 → conscio-2.4.0}/conscio/adapter_config.py +0 -0
  36. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/__init__.py +0 -0
  37. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/act.py +0 -0
  38. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/actor.py +0 -0
  39. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/adapter.py +0 -0
  40. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/adapters.py +0 -0
  41. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/breaker.py +0 -0
  42. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/contracts.py +0 -0
  43. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/fingerprint.py +0 -0
  44. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/gateway.py +0 -0
  45. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/grammar.py +0 -0
  46. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/host_act.py +0 -0
  47. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/ledger.py +0 -0
  48. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/loop.py +0 -0
  49. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/profiles.py +0 -0
  50. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/skeptic.py +0 -0
  51. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/tools.py +0 -0
  52. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/trial.py +0 -0
  53. {conscio-2.2.2 → conscio-2.4.0}/conscio/agency/trust.py +0 -0
  54. {conscio-2.2.2 → conscio-2.4.0}/conscio/auto_evolution.py +0 -0
  55. {conscio-2.2.2 → conscio-2.4.0}/conscio/axis_pack.py +0 -0
  56. {conscio-2.2.2 → conscio-2.4.0}/conscio/bench.py +0 -0
  57. {conscio-2.2.2 → conscio-2.4.0}/conscio/coherence.py +0 -0
  58. {conscio-2.2.2 → conscio-2.4.0}/conscio/content_layer.py +0 -0
  59. {conscio-2.2.2 → conscio-2.4.0}/conscio/content_store.py +0 -0
  60. {conscio-2.2.2 → conscio-2.4.0}/conscio/context_manager.py +0 -0
  61. {conscio-2.2.2 → conscio-2.4.0}/conscio/daemon.py +0 -0
  62. {conscio-2.2.2 → conscio-2.4.0}/conscio/dreaming.py +0 -0
  63. {conscio-2.2.2 → conscio-2.4.0}/conscio/event_bus.py +0 -0
  64. {conscio-2.2.2 → conscio-2.4.0}/conscio/goal_generator.py +0 -0
  65. {conscio-2.2.2 → conscio-2.4.0}/conscio/guards.py +0 -0
  66. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/__init__.py +0 -0
  67. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/config.py +0 -0
  68. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/model_test.py +0 -0
  69. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/providers.py +0 -0
  70. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/server.py +0 -0
  71. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/static/app.js +0 -0
  72. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/static/index.html +0 -0
  73. {conscio-2.2.2 → conscio-2.4.0}/conscio/hub/static/style.css +0 -0
  74. {conscio-2.2.2 → conscio-2.4.0}/conscio/inner_monologue.py +0 -0
  75. {conscio-2.2.2 → conscio-2.4.0}/conscio/mcp/__init__.py +0 -0
  76. {conscio-2.2.2 → conscio-2.4.0}/conscio/mcp/jsonrpc.py +0 -0
  77. {conscio-2.2.2 → conscio-2.4.0}/conscio/mcp/protocol.py +0 -0
  78. {conscio-2.2.2 → conscio-2.4.0}/conscio/mcp/seen.py +0 -0
  79. {conscio-2.2.2 → conscio-2.4.0}/conscio/meta_cognition.py +0 -0
  80. {conscio-2.2.2 → conscio-2.4.0}/conscio/metabolic.py +0 -0
  81. {conscio-2.2.2 → conscio-2.4.0}/conscio/migrate.py +0 -0
  82. {conscio-2.2.2 → conscio-2.4.0}/conscio/models.py +0 -0
  83. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/__init__.py +0 -0
  84. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/artifact.py +0 -0
  85. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/audit.py +0 -0
  86. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/catalog.py +0 -0
  87. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/cli.py +0 -0
  88. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/identity.py +0 -0
  89. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/importer.py +0 -0
  90. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/paths.py +0 -0
  91. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/publish.py +0 -0
  92. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/record.py +0 -0
  93. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/record_catalog.py +0 -0
  94. {conscio-2.2.2 → conscio-2.4.0}/conscio/noosphere/record_publish.py +0 -0
  95. {conscio-2.2.2 → conscio-2.4.0}/conscio/output_filter.py +0 -0
  96. {conscio-2.2.2 → conscio-2.4.0}/conscio/perception/__init__.py +0 -0
  97. {conscio-2.2.2 → conscio-2.4.0}/conscio/perception/agent_sensor.py +0 -0
  98. {conscio-2.2.2 → conscio-2.4.0}/conscio/perception/host_sensor.py +0 -0
  99. {conscio-2.2.2 → conscio-2.4.0}/conscio/perception/sensor.py +0 -0
  100. {conscio-2.2.2 → conscio-2.4.0}/conscio/plugins.py +0 -0
  101. {conscio-2.2.2 → conscio-2.4.0}/conscio/presets/__init__.py +0 -0
  102. {conscio-2.2.2 → conscio-2.4.0}/conscio/py.typed +0 -0
  103. {conscio-2.2.2 → conscio-2.4.0}/conscio/risk.py +0 -0
  104. {conscio-2.2.2 → conscio-2.4.0}/conscio/self_prompt.py +0 -0
  105. {conscio-2.2.2 → conscio-2.4.0}/conscio/semantic.py +0 -0
  106. {conscio-2.2.2 → conscio-2.4.0}/conscio/session_lifecycle.py +0 -0
  107. {conscio-2.2.2 → conscio-2.4.0}/conscio/session_rag.py +0 -0
  108. {conscio-2.2.2 → conscio-2.4.0}/conscio/session_rag_factory.py +0 -0
  109. {conscio-2.2.2 → conscio-2.4.0}/conscio/shard_engine.py +0 -0
  110. {conscio-2.2.2 → conscio-2.4.0}/conscio/structural.py +0 -0
  111. {conscio-2.2.2 → conscio-2.4.0}/conscio/structural_consent.py +0 -0
  112. {conscio-2.2.2 → conscio-2.4.0}/conscio/structural_drift.py +0 -0
  113. {conscio-2.2.2 → conscio-2.4.0}/conscio/timeutil.py +0 -0
  114. {conscio-2.2.2 → conscio-2.4.0}/conscio/token_tracker.py +0 -0
  115. {conscio-2.2.2 → conscio-2.4.0}/conscio/voice_preset.py +0 -0
  116. {conscio-2.2.2 → conscio-2.4.0}/conscio/workspace.py +0 -0
  117. {conscio-2.2.2 → conscio-2.4.0}/conscio/world_model.py +0 -0
  118. {conscio-2.2.2 → conscio-2.4.0}/conscio.egg-info/dependency_links.txt +0 -0
  119. {conscio-2.2.2 → conscio-2.4.0}/conscio.egg-info/requires.txt +0 -0
  120. {conscio-2.2.2 → conscio-2.4.0}/conscio.egg-info/top_level.txt +0 -0
  121. {conscio-2.2.2 → conscio-2.4.0}/setup.cfg +0 -0
  122. {conscio-2.2.2 → conscio-2.4.0}/tests/test_adapter_config.py +0 -0
  123. {conscio-2.2.2 → conscio-2.4.0}/tests/test_adapter_config_api_key_env.py +0 -0
  124. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_act.py +0 -0
  125. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_act_immunity.py +0 -0
  126. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_actor.py +0 -0
  127. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_adapter.py +0 -0
  128. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_adapters_http.py +0 -0
  129. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_adversarial.py +0 -0
  130. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_arbiter.py +0 -0
  131. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_bench.py +0 -0
  132. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_breaker.py +0 -0
  133. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_contracts.py +0 -0
  134. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_engine_immunity.py +0 -0
  135. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_gateway.py +0 -0
  136. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_grammar.py +0 -0
  137. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_host_act.py +0 -0
  138. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_ledger.py +0 -0
  139. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_ledger_v201.py +0 -0
  140. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_loop.py +0 -0
  141. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_no_network.py +0 -0
  142. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_profiles.py +0 -0
  143. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_skeptic.py +0 -0
  144. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_skills.py +0 -0
  145. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_skills_wiring.py +0 -0
  146. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_state.py +0 -0
  147. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_tools.py +0 -0
  148. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_tools_manifest.py +0 -0
  149. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_trial.py +0 -0
  150. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_trust.py +0 -0
  151. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agency_volition.py +0 -0
  152. {conscio-2.2.2 → conscio-2.4.0}/tests/test_agent_sensor.py +0 -0
  153. {conscio-2.2.2 → conscio-2.4.0}/tests/test_auto_evolution.py +0 -0
  154. {conscio-2.2.2 → conscio-2.4.0}/tests/test_awake.py +0 -0
  155. {conscio-2.2.2 → conscio-2.4.0}/tests/test_axis_pack.py +0 -0
  156. {conscio-2.2.2 → conscio-2.4.0}/tests/test_axis_pack_battery.py +0 -0
  157. {conscio-2.2.2 → conscio-2.4.0}/tests/test_cli.py +0 -0
  158. {conscio-2.2.2 → conscio-2.4.0}/tests/test_cli_trial.py +0 -0
  159. {conscio-2.2.2 → conscio-2.4.0}/tests/test_coherence.py +0 -0
  160. {conscio-2.2.2 → conscio-2.4.0}/tests/test_coherence_engine_integration.py +0 -0
  161. {conscio-2.2.2 → conscio-2.4.0}/tests/test_coherence_state.py +0 -0
  162. {conscio-2.2.2 → conscio-2.4.0}/tests/test_consciousness.py +0 -0
  163. {conscio-2.2.2 → conscio-2.4.0}/tests/test_content_layer.py +0 -0
  164. {conscio-2.2.2 → conscio-2.4.0}/tests/test_content_layer_manager.py +0 -0
  165. {conscio-2.2.2 → conscio-2.4.0}/tests/test_content_store.py +0 -0
  166. {conscio-2.2.2 → conscio-2.4.0}/tests/test_content_store_battery.py +0 -0
  167. {conscio-2.2.2 → conscio-2.4.0}/tests/test_content_store_r05.py +0 -0
  168. {conscio-2.2.2 → conscio-2.4.0}/tests/test_contradiction_detector.py +0 -0
  169. {conscio-2.2.2 → conscio-2.4.0}/tests/test_daemon.py +0 -0
  170. {conscio-2.2.2 → conscio-2.4.0}/tests/test_daemon_battery.py +0 -0
  171. {conscio-2.2.2 → conscio-2.4.0}/tests/test_daemon_config.py +0 -0
  172. {conscio-2.2.2 → conscio-2.4.0}/tests/test_daemon_heartbeat.py +0 -0
  173. {conscio-2.2.2 → conscio-2.4.0}/tests/test_daemon_structure_sync.py +0 -0
  174. {conscio-2.2.2 → conscio-2.4.0}/tests/test_dream_coherence.py +0 -0
  175. {conscio-2.2.2 → conscio-2.4.0}/tests/test_dream_distill.py +0 -0
  176. {conscio-2.2.2 → conscio-2.4.0}/tests/test_dream_reconcile.py +0 -0
  177. {conscio-2.2.2 → conscio-2.4.0}/tests/test_dreaming.py +0 -0
  178. {conscio-2.2.2 → conscio-2.4.0}/tests/test_dreaming_friction.py +0 -0
  179. {conscio-2.2.2 → conscio-2.4.0}/tests/test_durable_guards.py +0 -0
  180. {conscio-2.2.2 → conscio-2.4.0}/tests/test_embedder.py +0 -0
  181. {conscio-2.2.2 → conscio-2.4.0}/tests/test_engine_advisory.py +0 -0
  182. {conscio-2.2.2 → conscio-2.4.0}/tests/test_engine_host_act.py +0 -0
  183. {conscio-2.2.2 → conscio-2.4.0}/tests/test_engine_init.py +0 -0
  184. {conscio-2.2.2 → conscio-2.4.0}/tests/test_engine_meta_reflect.py +0 -0
  185. {conscio-2.2.2 → conscio-2.4.0}/tests/test_engine_propose.py +0 -0
  186. {conscio-2.2.2 → conscio-2.4.0}/tests/test_engine_recall.py +0 -0
  187. {conscio-2.2.2 → conscio-2.4.0}/tests/test_engine_trial.py +0 -0
  188. {conscio-2.2.2 → conscio-2.4.0}/tests/test_event_bus.py +0 -0
  189. {conscio-2.2.2 → conscio-2.4.0}/tests/test_event_bus_act_result.py +0 -0
  190. {conscio-2.2.2 → conscio-2.4.0}/tests/test_event_bus_purge.py +0 -0
  191. {conscio-2.2.2 → conscio-2.4.0}/tests/test_examples.py +0 -0
  192. {conscio-2.2.2 → conscio-2.4.0}/tests/test_goal_provenance.py +0 -0
  193. {conscio-2.2.2 → conscio-2.4.0}/tests/test_goal_source.py +0 -0
  194. {conscio-2.2.2 → conscio-2.4.0}/tests/test_host_sensor.py +0 -0
  195. {conscio-2.2.2 → conscio-2.4.0}/tests/test_hub_config.py +0 -0
  196. {conscio-2.2.2 → conscio-2.4.0}/tests/test_hub_model_test.py +0 -0
  197. {conscio-2.2.2 → conscio-2.4.0}/tests/test_hub_providers.py +0 -0
  198. {conscio-2.2.2 → conscio-2.4.0}/tests/test_hub_server.py +0 -0
  199. {conscio-2.2.2 → conscio-2.4.0}/tests/test_loop_failure_brake.py +0 -0
  200. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_adapter_parity.py +0 -0
  201. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_battery.py +0 -0
  202. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_fuzz.py +0 -0
  203. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_jsonrpc.py +0 -0
  204. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_package.py +0 -0
  205. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_protocol.py +0 -0
  206. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_schemas.py +0 -0
  207. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_seen.py +0 -0
  208. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_server.py +0 -0
  209. {conscio-2.2.2 → conscio-2.4.0}/tests/test_mcp_server_cli.py +0 -0
  210. {conscio-2.2.2 → conscio-2.4.0}/tests/test_metabolic.py +0 -0
  211. {conscio-2.2.2 → conscio-2.4.0}/tests/test_migrate.py +0 -0
  212. {conscio-2.2.2 → conscio-2.4.0}/tests/test_model_auto_detect.py +0 -0
  213. {conscio-2.2.2 → conscio-2.4.0}/tests/test_model_offline_default.py +0 -0
  214. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_artifact.py +0 -0
  215. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_audit.py +0 -0
  216. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_audit_integration.py +0 -0
  217. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_catalog.py +0 -0
  218. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_cli.py +0 -0
  219. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_engine_free.py +0 -0
  220. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_fingerprint.py +0 -0
  221. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_identity.py +0 -0
  222. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_importer.py +0 -0
  223. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_integration.py +0 -0
  224. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_parity.py +0 -0
  225. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_paths.py +0 -0
  226. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_publish.py +0 -0
  227. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_quarantine.py +0 -0
  228. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_record.py +0 -0
  229. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_record_catalog.py +0 -0
  230. {conscio-2.2.2 → conscio-2.4.0}/tests/test_noosphere_record_publish.py +0 -0
  231. {conscio-2.2.2 → conscio-2.4.0}/tests/test_ontological_semantic.py +0 -0
  232. {conscio-2.2.2 → conscio-2.4.0}/tests/test_output_filter.py +0 -0
  233. {conscio-2.2.2 → conscio-2.4.0}/tests/test_output_filter_stages.py +0 -0
  234. {conscio-2.2.2 → conscio-2.4.0}/tests/test_packaging.py +0 -0
  235. {conscio-2.2.2 → conscio-2.4.0}/tests/test_perception.py +0 -0
  236. {conscio-2.2.2 → conscio-2.4.0}/tests/test_perf_reflection.py +0 -0
  237. {conscio-2.2.2 → conscio-2.4.0}/tests/test_plugins.py +0 -0
  238. {conscio-2.2.2 → conscio-2.4.0}/tests/test_quarantine_trial.py +0 -0
  239. {conscio-2.2.2 → conscio-2.4.0}/tests/test_recursive_loop_integration.py +0 -0
  240. {conscio-2.2.2 → conscio-2.4.0}/tests/test_risk.py +0 -0
  241. {conscio-2.2.2 → conscio-2.4.0}/tests/test_self_prompt.py +0 -0
  242. {conscio-2.2.2 → conscio-2.4.0}/tests/test_self_prompt_state.py +0 -0
  243. {conscio-2.2.2 → conscio-2.4.0}/tests/test_semantic_dedup.py +0 -0
  244. {conscio-2.2.2 → conscio-2.4.0}/tests/test_semantic_engine.py +0 -0
  245. {conscio-2.2.2 → conscio-2.4.0}/tests/test_semantic_reconcile_integration.py +0 -0
  246. {conscio-2.2.2 → conscio-2.4.0}/tests/test_session_lifecycle.py +0 -0
  247. {conscio-2.2.2 → conscio-2.4.0}/tests/test_session_lifecycle_battery.py +0 -0
  248. {conscio-2.2.2 → conscio-2.4.0}/tests/test_session_lifecycle_dream.py +0 -0
  249. {conscio-2.2.2 → conscio-2.4.0}/tests/test_session_rag.py +0 -0
  250. {conscio-2.2.2 → conscio-2.4.0}/tests/test_shard_engine.py +0 -0
  251. {conscio-2.2.2 → conscio-2.4.0}/tests/test_state_loaders_battery.py +0 -0
  252. {conscio-2.2.2 → conscio-2.4.0}/tests/test_state_persistence_battery.py +0 -0
  253. {conscio-2.2.2 → conscio-2.4.0}/tests/test_structural.py +0 -0
  254. {conscio-2.2.2 → conscio-2.4.0}/tests/test_structural_consent.py +0 -0
  255. {conscio-2.2.2 → conscio-2.4.0}/tests/test_structural_drift.py +0 -0
  256. {conscio-2.2.2 → conscio-2.4.0}/tests/test_structural_inject.py +0 -0
  257. {conscio-2.2.2 → conscio-2.4.0}/tests/test_timeutil.py +0 -0
  258. {conscio-2.2.2 → conscio-2.4.0}/tests/test_token_tracker.py +0 -0
  259. {conscio-2.2.2 → conscio-2.4.0}/tests/test_trajectory_vector.py +0 -0
  260. {conscio-2.2.2 → conscio-2.4.0}/tests/test_v2_debt_zero.py +0 -0
  261. {conscio-2.2.2 → conscio-2.4.0}/tests/test_voice_preset.py +0 -0
  262. {conscio-2.2.2 → conscio-2.4.0}/tests/test_workspace.py +0 -0
  263. {conscio-2.2.2 → conscio-2.4.0}/tests/test_world_contradictions.py +0 -0
  264. {conscio-2.2.2 → conscio-2.4.0}/tests/test_world_model_entropy.py +0 -0
  265. {conscio-2.2.2 → conscio-2.4.0}/tests/test_world_model_prediction.py +0 -0
  266. {conscio-2.2.2 → conscio-2.4.0}/tests/test_world_model_prune.py +0 -0
@@ -7,6 +7,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [2.4.0] - 2026-06-23 — "Observatory"
11
+
12
+ ### Added
13
+ - **Read-only Observatory.** `conscio-observatory --storage DIR [--token TOK]`
14
+ serves a **loopback-only** HTTP viewer of one instance's persisted state —
15
+ logs (events), goals, actions (ledger), skills, and the last state snapshot.
16
+ It is **engine-free** and **read-only**: a new `conscio/observatory/projection.py`
17
+ opens `conscio.db` with `mode=ro` (no `PRAGMA`, `SELECT` only) and parses
18
+ `goals.json` / `state_summary.json`. The server serves **GET only** — every
19
+ mutation verb returns **405**. It reads even a **cold** instance (no engine).
20
+ - **MCP read-only state tools.** `conscio.state`, `conscio.events`,
21
+ `conscio.handoff` are now exposed as MCP `tools/call` entries (for hosts that
22
+ speak tools, not resources), delegating to the **same** helpers the
23
+ `conscio://` resources use. Always on, propose-only grade, independent of
24
+ `--enable-act`.
25
+ - **No `--enable-observatory` flag.** The Observatory has no write/execute/
26
+ autonomy surface, so launching the command *is* the opt-in; it is independent
27
+ of `--enable-act` and `--awake`.
28
+
29
+ ### Notes
30
+ - **Two freshness contracts:** the MCP tools read the **live** engine; the
31
+ viewer reads the **last-persisted** snapshot (may lag — the UI says so).
32
+ - **Debt-zero:** the Hub (`conscio/hub/`) is untouched; the Observatory is a
33
+ separate, self-contained package. The shared **noosphere** view is deferred to
34
+ v2.5 (the projection is built source-pluggable for it).
35
+ - **Multi-user hosts:** on a shared machine, pass `--token` — loopback blocks
36
+ remote access but not other local users.
37
+
38
+ ## [2.3.0] - 2026-06-23 — "Promotion"
39
+
40
+ ### Added
41
+ - **Promotion-to-live.** `conscio promote --storage DIR --quarantine ROWID
42
+ --enable-promote` graduates a quarantined foreign skill that has earned
43
+ **≥ 3 clean local trials** (v2.2.2) into the live `SkillLibrary`, seeded with
44
+ the counters it earned **locally** in the sandbox — never the origin's
45
+ (stripped) stats, so no trust is inherited. Engine-side
46
+ (`engine.promote_quarantined`); the noosphere package stays engine-free and
47
+ read-only on `conscio.db`.
48
+ - **Tool-existence check** (deferred from v2.2). Promotion refuses a skill
49
+ whose plan names a tool this instance's live registry does not have.
50
+ - `SkillLibrary.graft` — the single foreign-skill write seam
51
+ (`ON CONFLICT(goal_fp, tool_seq) DO NOTHING`; never overwrites a local skill).
52
+ - Quarantine rows gain `promoted_ts` / `promoted_skill_id` (idempotency +
53
+ audit). Promotion re-checks the content hash (tamper → refuse, no write) and
54
+ is idempotent. `--enable-promote` is required, default off, and **independent
55
+ of** `--enable-trial` / `--enable-act`.
56
+
57
+ ---
58
+
10
59
  ## [2.2.2] - 2026-06-22 — "Trial / execution path"
11
60
 
12
61
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conscio
3
- Version: 2.2.2
3
+ Version: 2.4.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.2.2` — "Trial / execution path": a quarantined imported skill can now **prove itself locally** before any promotion. `conscio trial --quarantine ROWID --enable-trial` replays the foreign plan's fixed steps in a **throwaway, fs-only sandbox** through the full safety stack (`validate precheck HIGH-block Skepticdispatch`), recording a binary pass/fail on the quarantine row. Fully **isolated** never writes the live agent's ledger/skills/trust/breaker; tamper refuses without counting. Off by default; independent of `--enable-act`. `pip install conscio`.
50
- - **Prior:** `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.
49
+ - **Current release:** `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`.
50
+ - **Prior:** `v2.3.0` — "Promotion": a quarantined skill that has earned **≥ 3 clean local trials** can now be **graduated into the live skill library**. `conscio promote --quarantine ROWID --enable-promote` re-checks the content hash, verifies every tool in the plan exists locally (**tool-existence check**), and grafts the skill seeded with the counters it earned in the sandbox **never the origin's stats**, so no trust is inherited. Engine-side write; the noosphere stays engine-free and read-only on `conscio.db`. Never overwrites a local skill; idempotent; off by default and independent of `--enable-trial`/`--enable-act`.
51
+ - **Earlier:** `v2.2.2` — "Trial / execution path": a quarantined imported skill can **prove itself locally** before promotion — `conscio trial --quarantine ROWID --enable-trial` replays the fixed foreign plan in a **throwaway, fs-only sandbox** through the full safety stack (`validate → precheck → HIGH-block → Skeptic → dispatch`), recording a binary pass/fail on the quarantine row. Fully **isolated** — never writes the live agent's ledger/skills/trust/breaker; tamper refuses without counting. Off by default; independent of `--enable-act`.
52
+ - **Earlier:** `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.
51
53
  - **Earlier:** `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.
52
54
  - **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.
53
55
  - **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.
@@ -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.2.2` — "Trial / execution path": a quarantined imported skill can now **prove itself locally** before any promotion. `conscio trial --quarantine ROWID --enable-trial` replays the foreign plan's fixed steps in a **throwaway, fs-only sandbox** through the full safety stack (`validate precheck HIGH-block Skepticdispatch`), recording a binary pass/fail on the quarantine row. Fully **isolated** never writes the live agent's ledger/skills/trust/breaker; tamper refuses without counting. Off by default; independent of `--enable-act`. `pip install conscio`.
15
- - **Prior:** `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.
14
+ - **Current release:** `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`.
15
+ - **Prior:** `v2.3.0` — "Promotion": a quarantined skill that has earned **≥ 3 clean local trials** can now be **graduated into the live skill library**. `conscio promote --quarantine ROWID --enable-promote` re-checks the content hash, verifies every tool in the plan exists locally (**tool-existence check**), and grafts the skill seeded with the counters it earned in the sandbox **never the origin's stats**, so no trust is inherited. Engine-side write; the noosphere stays engine-free and read-only on `conscio.db`. Never overwrites a local skill; idempotent; off by default and independent of `--enable-trial`/`--enable-act`.
16
+ - **Earlier:** `v2.2.2` — "Trial / execution path": a quarantined imported skill can **prove itself locally** before promotion — `conscio trial --quarantine ROWID --enable-trial` replays the fixed foreign plan in a **throwaway, fs-only sandbox** through the full safety stack (`validate → precheck → HIGH-block → Skeptic → dispatch`), recording a binary pass/fail on the quarantine row. Fully **isolated** — never writes the live agent's ledger/skills/trust/breaker; tamper refuses without counting. Off by default; independent of `--enable-act`.
17
+ - **Earlier:** `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.
16
18
  - **Earlier:** `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.
17
19
  - **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.
18
20
  - **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.
@@ -6,7 +6,7 @@ and goal generation. Adapts behavior based on the underlying model's
6
6
  context window size.
7
7
  """
8
8
 
9
- __version__ = "2.2.2"
9
+ __version__ = "2.4.0"
10
10
  __author__ = "Neguiolidas / Neguitech"
11
11
 
12
12
  from .engine import ConsciousnessEngine
@@ -0,0 +1,55 @@
1
+ # conscio/agency/promote.py
2
+ """Promotion gate for a quarantined foreign skill (v2.3.0).
3
+
4
+ A pure mechanic: given a quarantined row's trial counters and tool sequence,
5
+ decide whether it has earned graduation into the live SkillLibrary. Imports
6
+ nothing from conscio.noosphere or conscio.engine. The caller (the engine) owns
7
+ the read of the quarantine row, the graft into the library, and the
8
+ promoted-stamp; this module only judges."""
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from dataclasses import dataclass
13
+ from typing import Any
14
+
15
+ MIN_PROMOTE_PASSES = 3 # clean local trials required before graduation
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class PromoteResult:
20
+ skill_id: int
21
+ successes: int
22
+ failures: int
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class PromoteRefusal:
27
+ reason: str
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class PromoteDecision:
32
+ ok: bool
33
+ reason: str # "" when ok
34
+
35
+
36
+ def evaluate_promotion(*, trial_successes: int, trial_failures: int,
37
+ tool_seq: str, registry: Any) -> PromoteDecision:
38
+ """Gate a quarantined row for promotion. Order: enough clean passes, zero
39
+ failures, decodable tool_seq, every tool present in the live registry."""
40
+ if trial_successes < MIN_PROMOTE_PASSES:
41
+ return PromoteDecision(
42
+ False,
43
+ f"insufficient trials ({trial_successes}/{MIN_PROMOTE_PASSES})")
44
+ if trial_failures != 0:
45
+ return PromoteDecision(False, f"failed {trial_failures} trial(s)")
46
+ try:
47
+ tools = json.loads(tool_seq)
48
+ if not isinstance(tools, list):
49
+ raise ValueError("tool_seq is not a list")
50
+ except (ValueError, TypeError) as exc:
51
+ return PromoteDecision(False, f"corrupt tool_seq: {exc}")
52
+ for tool in tools:
53
+ if registry.get(str(tool)) is None:
54
+ return PromoteDecision(False, f"unknown tool '{tool}'")
55
+ return PromoteDecision(True, "")
@@ -166,6 +166,26 @@ class SkillLibrary:
166
166
  [(skill_id,) for skill_id in served])
167
167
  self._conn.commit()
168
168
 
169
+ # ── promotion (v2.3) ────────────────────────────────────────────────
170
+
171
+ def graft(self, goal_fp: str, goal_text: str, tool_seq: str,
172
+ plan_template: str, *, successes: int,
173
+ failures: int) -> int | None:
174
+ """Insert a promoted foreign skill as data, seeded with the trial
175
+ counters it earned locally. The single write seam for foreign skills.
176
+ Never overwrites a local skill sharing (goal_fp, tool_seq): a collision
177
+ returns None."""
178
+ cur = self._conn.execute(
179
+ "INSERT INTO skills (created_ts, goal_fp, goal_text, tool_seq,"
180
+ " plan_template, successes, failures) VALUES (?, ?, ?, ?, ?, ?, ?)"
181
+ " ON CONFLICT(goal_fp, tool_seq) DO NOTHING",
182
+ (time.time(), goal_fp, goal_text, tool_seq, plan_template,
183
+ successes, failures))
184
+ self._conn.commit()
185
+ if cur.rowcount == 0 or cur.lastrowid is None:
186
+ return None
187
+ return int(cur.lastrowid)
188
+
169
189
  # ── rendering ──────────────────────────────────────────────────────
170
190
 
171
191
  @staticmethod
@@ -80,6 +80,18 @@ def _build_parser() -> argparse.ArgumentParser:
80
80
  "--enable-trial", action="store_true",
81
81
  help="required: actually run the sandboxed trial (off by default)")
82
82
 
83
+ p_promote = sub.add_parser(
84
+ "promote",
85
+ help="promote a trialed quarantined skill into the live library")
86
+ p_promote.add_argument("--storage", default="",
87
+ help="instance storage dir (default: ~/.hermes)")
88
+ p_promote.add_argument("--quarantine", type=int, required=True,
89
+ metavar="ROWID",
90
+ help="quarantine row id to promote")
91
+ p_promote.add_argument(
92
+ "--enable-promote", action="store_true",
93
+ help="required: actually write to the live library (off by default)")
94
+
83
95
  # Listed for discoverability; routed to conscio.{bench,daemon} before argparse.
84
96
  sub.add_parser("bench", add_help=False,
85
97
  help="measure an inference backend (see: conscio bench --help)")
@@ -292,6 +304,36 @@ def _cmd_trial(model: str, storage: str, quarantine_id: int,
292
304
  return 0
293
305
 
294
306
 
307
+ def _run_promote(*, storage: str, quarantine_id: int, enable_promote: bool):
308
+ """Build a bare engine (no adapter — promotion never decodes) and promote
309
+ one quarantined skill. The single seam the CLI tests monkeypatch."""
310
+ from .engine import ConsciousnessEngine
311
+ eng = ConsciousnessEngine(model_name=DEFAULT_MODEL,
312
+ storage_path=_storage(storage))
313
+ try:
314
+ return eng.promote_quarantined(quarantine_id,
315
+ enable_promote=enable_promote)
316
+ finally:
317
+ eng.close()
318
+
319
+
320
+ def _cmd_promote(storage: str, quarantine_id: int,
321
+ enable_promote: bool) -> int:
322
+ from .agency.promote import PromoteResult
323
+ try:
324
+ outcome = _run_promote(storage=storage, quarantine_id=quarantine_id,
325
+ enable_promote=enable_promote)
326
+ except Exception as exc: # engine wiring failure
327
+ print(f"error: {exc}")
328
+ return 1
329
+ if isinstance(outcome, PromoteResult):
330
+ print(f"PROMOTED skill #{outcome.skill_id} "
331
+ f"(seeded {outcome.successes}/{outcome.failures})")
332
+ return 0
333
+ print(f"PROMOTE REFUSED: {outcome.reason}")
334
+ return 1
335
+
336
+
295
337
  def main(argv: list[str] | None = None) -> int:
296
338
  argv = list(sys.argv[1:] if argv is None else argv)
297
339
 
@@ -330,6 +372,9 @@ def main(argv: list[str] | None = None) -> int:
330
372
  if args.command == "trial":
331
373
  return _cmd_trial(args.model, args.storage, args.quarantine,
332
374
  args.enable_trial)
375
+ if args.command == "promote":
376
+ return _cmd_promote(args.storage, args.quarantine,
377
+ args.enable_promote)
333
378
 
334
379
  parser.print_help()
335
380
  return 2
@@ -1064,6 +1064,79 @@ class ConsciousnessEngine:
1064
1064
  ts=time.time())
1065
1065
  return outcome
1066
1066
 
1067
+ # --- v2.3 "Promotion": graduate a quarantined skill into the library ---
1068
+
1069
+ def promote_quarantined(self, rowid: int, *, enable_promote: bool = False):
1070
+ """Graduate a quarantined foreign skill that has earned >= 3 clean
1071
+ local trials into the live SkillLibrary, seeded with its trial
1072
+ counters. No adapter, no sandbox, no execution: a data gate that reads
1073
+ the trial evidence and writes one row. Default off.
1074
+
1075
+ Unlike trial (which records a tamper via note_trial), promotion refuses
1076
+ a tampered/already-promoted/gate-failed row WITHOUT writing anything —
1077
+ it only ever writes on a successful graft (the skills row + the
1078
+ promoted-stamp). A refusal leaves the quarantine row untouched.
1079
+
1080
+ Returns a promote.PromoteResult (promoted) or promote.PromoteRefusal."""
1081
+ import time
1082
+
1083
+ from .agency import promote as promote_mod
1084
+ from .agency.skills import SkillLibrary
1085
+ from .agency.tools import make_default_registry
1086
+ from .noosphere import artifact, quarantine
1087
+ from .noosphere.paths import quarantine_db_path
1088
+
1089
+ if not enable_promote:
1090
+ return promote_mod.PromoteRefusal(
1091
+ "promotion disabled; pass --enable-promote")
1092
+
1093
+ qdb = quarantine_db_path(self.storage)
1094
+ row = quarantine.get(qdb, rowid)
1095
+ if row is None:
1096
+ return promote_mod.PromoteRefusal(f"no quarantine row #{rowid}")
1097
+ if row.import_status != "quarantined":
1098
+ return promote_mod.PromoteRefusal(
1099
+ f"row #{rowid} is not quarantined "
1100
+ f"(status={row.import_status})")
1101
+ if row.promoted_ts > 0:
1102
+ return promote_mod.PromoteRefusal(
1103
+ f"row #{rowid} already promoted "
1104
+ f"(skill #{row.promoted_skill_id})")
1105
+ if artifact.content_hash(row.artifact_json) != row.content_sha256:
1106
+ return promote_mod.PromoteRefusal(
1107
+ f"row #{rowid} tampered (content_sha256 mismatch)")
1108
+
1109
+ # Live tool registry, real backends. Used ONLY for registry.get(tool)
1110
+ # existence checks — nothing is dispatched, so no tmpdir is needed (the
1111
+ # one structural difference from trial). make_default_registry mkdirs
1112
+ # sandbox_root, so it points inside the instance storage (never real
1113
+ # home), keeping promotion and its tests side-effect-clean.
1114
+ reg = make_default_registry(
1115
+ sandbox_root=self.storage / "sandbox",
1116
+ content_store=self.content_store, event_bus=self.event_bus,
1117
+ goal_generator=self.goals)
1118
+ decision = promote_mod.evaluate_promotion(
1119
+ trial_successes=row.trial_successes,
1120
+ trial_failures=row.trial_failures,
1121
+ tool_seq=row.tool_seq, registry=reg)
1122
+ if not decision.ok:
1123
+ return promote_mod.PromoteRefusal(decision.reason)
1124
+
1125
+ lib = SkillLibrary(self.storage / "conscio.db")
1126
+ try:
1127
+ sid = lib.graft(row.goal_fp, row.goal_text, row.tool_seq,
1128
+ row.plan_template, successes=row.trial_successes,
1129
+ failures=row.trial_failures)
1130
+ finally:
1131
+ lib.close()
1132
+ if sid is None:
1133
+ return promote_mod.PromoteRefusal(
1134
+ "skill already present (goal_fp, tool_seq collision)")
1135
+
1136
+ quarantine.mark_promoted(qdb, rowid, ts=time.time(), skill_id=sid)
1137
+ return promote_mod.PromoteResult(sid, row.trial_successes,
1138
+ row.trial_failures)
1139
+
1067
1140
  # --- v2.0 "Connect": propose-only cognition (never executes) ---
1068
1141
 
1069
1142
  def propose_action(self, intent: dict) -> dict:
@@ -86,6 +86,19 @@ BASE_TOOL_DEFS: list[dict] = [
86
86
  "properties": {"goal": {"type": "string"},
87
87
  "tools": {"type": "array"}},
88
88
  "required": ["goal", "tools"]}},
89
+ {"name": "conscio.state",
90
+ "description": "ConsciousnessState snapshot (pure read).",
91
+ "inputSchema": {"type": "object", "properties": {}}},
92
+ {"name": "conscio.events",
93
+ "description": "Recent events (pure read; type/category/since/limit).",
94
+ "inputSchema": {"type": "object",
95
+ "properties": {"type": {"type": "string"},
96
+ "category": {"type": "string"},
97
+ "since": {"type": "string"},
98
+ "limit": {"type": "integer"}}}},
99
+ {"name": "conscio.handoff",
100
+ "description": "Latest session handoff (pure read, markdown).",
101
+ "inputSchema": {"type": "object", "properties": {}}},
89
102
  ]
90
103
 
91
104
  RESOURCE_DEFS: list[dict] = [
@@ -94,6 +94,9 @@ class Bindings:
94
94
  self._require(a, "intent")),
95
95
  "conscio.propose_plan": lambda a: self.engine.propose_plan(
96
96
  self._require(a, "goal"), a.get("tools")),
97
+ "conscio.state": lambda a: self._state_payload(),
98
+ "conscio.events": lambda a: self._events_payload(a),
99
+ "conscio.handoff": lambda a: self._handoff_payload(),
97
100
  }
98
101
  if self._act_enabled():
99
102
  tools.update({
@@ -187,6 +190,25 @@ class Bindings:
187
190
  self.seen.mark(skey, json.dumps(result), time.time())
188
191
  return result
189
192
 
193
+ # ── read-only state payloads (shared by resources + tools, v2.4) ──
194
+ def _state_payload(self) -> dict:
195
+ return self.engine.advisory().get("state", {})
196
+
197
+ def _events_payload(self, params: dict) -> list[dict]:
198
+ def s(key: str) -> str | None:
199
+ v = params.get(key)
200
+ return str(v) if v not in (None, "") else None
201
+ try:
202
+ limit = int(params.get("limit", 50))
203
+ except (TypeError, ValueError):
204
+ limit = 50
205
+ rows = self.engine.event_bus.query(
206
+ type=s("type"), category=s("category"), since=s("since"), limit=limit)
207
+ return [e.to_dict() for e in rows]
208
+
209
+ def _handoff_payload(self) -> str:
210
+ return self._handoff_text()
211
+
190
212
  # ── resources ──
191
213
  def read_resource(self, uri: str) -> dict:
192
214
  parsed = urlparse(uri)
@@ -194,24 +216,14 @@ class Bindings:
194
216
  if base == "conscio://advisory":
195
217
  return self._json_resource(uri, self.engine.advisory())
196
218
  if base == "conscio://state":
197
- return self._json_resource(uri,
198
- self.engine.advisory().get("state", {}))
219
+ return self._json_resource(uri, self._state_payload())
199
220
  if base == "conscio://events":
200
221
  q = parse_qs(parsed.query)
201
-
202
- def first(key: str) -> str | None:
203
- vals = q.get(key)
204
- return vals[0] if vals else None
205
-
206
- rows = self.engine.event_bus.query(
207
- type=first("type"),
208
- category=first("category"),
209
- since=first("since"),
210
- limit=int(first("limit") or "50"))
211
- return self._json_resource(uri, [e.to_dict() for e in rows])
222
+ params = {k: (v[0] if v else None) for k, v in q.items()}
223
+ return self._json_resource(uri, self._events_payload(params))
212
224
  if base == "conscio://handoff":
213
225
  return {"contents": [{"uri": uri, "mimeType": "text/markdown",
214
- "text": self._handoff_text()}]}
226
+ "text": self._handoff_payload()}]}
215
227
  raise j.InvalidParams(f"unknown resource '{uri}'")
216
228
 
217
229
  @staticmethod
@@ -33,6 +33,8 @@ CREATE TABLE IF NOT EXISTS quarantine (
33
33
  last_trial_ts REAL NOT NULL DEFAULT 0,
34
34
  last_trial_result TEXT NOT NULL DEFAULT '',
35
35
  last_trial_error TEXT NOT NULL DEFAULT '',
36
+ promoted_ts REAL NOT NULL DEFAULT 0,
37
+ promoted_skill_id INTEGER NOT NULL DEFAULT 0,
36
38
  UNIQUE(origin_instance_id, content_sha256)
37
39
  );
38
40
  """
@@ -46,12 +48,18 @@ _TRIAL_COLS = {
46
48
  "last_trial_error": "TEXT NOT NULL DEFAULT ''",
47
49
  }
48
50
 
51
+ # v2.3: promotion-stats columns added to pre-existing DBs via _migrate.
52
+ _PROMOTE_COLS = {
53
+ "promoted_ts": "REAL NOT NULL DEFAULT 0",
54
+ "promoted_skill_id": "INTEGER NOT NULL DEFAULT 0",
55
+ }
56
+
49
57
 
50
58
  def _migrate(conn: sqlite3.Connection) -> None:
51
- """Idempotent ADD COLUMN for pre-v2.2.2 DBs. SQLite has no ADD COLUMN IF
52
- NOT EXISTS, so guard with PRAGMA table_info."""
59
+ """Idempotent ADD COLUMN for pre-v2.2.2/v2.3 DBs. SQLite has no ADD COLUMN
60
+ IF NOT EXISTS, so guard with PRAGMA table_info."""
53
61
  have = {r["name"] for r in conn.execute("PRAGMA table_info(quarantine)")}
54
- for col, decl in _TRIAL_COLS.items():
62
+ for col, decl in {**_TRIAL_COLS, **_PROMOTE_COLS}.items():
55
63
  if col not in have:
56
64
  conn.execute(f"ALTER TABLE quarantine ADD COLUMN {col} {decl}")
57
65
 
@@ -78,6 +86,8 @@ class QuarantineRow:
78
86
  last_trial_ts: float = 0.0
79
87
  last_trial_result: str = ""
80
88
  last_trial_error: str = ""
89
+ promoted_ts: float = 0.0
90
+ promoted_skill_id: int = 0
81
91
  id: int | None = None
82
92
 
83
93
 
@@ -116,7 +126,8 @@ def _row(r: sqlite3.Row) -> QuarantineRow:
116
126
  revalidation_error=r["revalidation_error"], schema_version=r["schema_version"],
117
127
  trial_successes=r["trial_successes"], trial_failures=r["trial_failures"],
118
128
  last_trial_ts=r["last_trial_ts"], last_trial_result=r["last_trial_result"],
119
- last_trial_error=r["last_trial_error"])
129
+ last_trial_error=r["last_trial_error"],
130
+ promoted_ts=r["promoted_ts"], promoted_skill_id=r["promoted_skill_id"])
120
131
 
121
132
 
122
133
  def insert(db: Path, row: QuarantineRow) -> bool:
@@ -196,3 +207,18 @@ def note_trial(db: Path, rowid: int, *, result: str, error: str,
196
207
  return cur.rowcount > 0
197
208
  finally:
198
209
  conn.close()
210
+
211
+
212
+ def mark_promoted(db: Path, rowid: int, *, ts: float, skill_id: int) -> bool:
213
+ """Stamp promoted_ts / promoted_skill_id after a successful graft into the
214
+ live library. Returns True iff a row was updated. Never touches the trial
215
+ counters or import_status."""
216
+ conn = _connect(db)
217
+ try:
218
+ cur = conn.execute(
219
+ "UPDATE quarantine SET promoted_ts = ?, promoted_skill_id = ?"
220
+ " WHERE id = ?", (ts, skill_id, rowid))
221
+ conn.commit()
222
+ return cur.rowcount > 0
223
+ finally:
224
+ conn.close()
@@ -0,0 +1,2 @@
1
+ """Conscio Observatory — engine-free, read-only view of one instance's
2
+ persisted state (v2.4). See conscio/observatory/projection.py."""
@@ -0,0 +1,97 @@
1
+ """Engine-free read-only projection of one instance's persisted state.
2
+
3
+ Opens conscio.db with mode=ro (NO PRAGMA, SELECT only) for events/actions/skills
4
+ and parses two JSON state files (goals.json is a LIST; state_summary.json is an
5
+ OBJECT). Never writes. Source-pluggable: a v2.5 noosphere source adds its own
6
+ method group + path without touching the conscio.db / JSON paths here."""
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import sqlite3
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from ..guards import clamp_int, safe_read_json # leaf utils; not conscio.engine
15
+
16
+
17
+ def _read_json_list(path: Path) -> list[dict]:
18
+ """safe_read_json is dict-only; goals.json is a list. Read a JSON list
19
+ safely: any problem (missing, OSError, malformed, not-a-list) -> []."""
20
+ try:
21
+ data = json.loads(path.read_text())
22
+ except (OSError, ValueError):
23
+ return []
24
+ return data if isinstance(data, list) else []
25
+
26
+
27
+ class Projection:
28
+ def __init__(self, storage: Path) -> None:
29
+ self.storage = Path(storage)
30
+ self._db = self.storage / "conscio.db"
31
+
32
+ # ── conscio.db (read-only) ──
33
+ def _ro(self) -> sqlite3.Connection:
34
+ conn = sqlite3.connect(f"file:{self._db}?mode=ro", uri=True)
35
+ conn.row_factory = sqlite3.Row
36
+ return conn # NO PRAGMA — mode=ro blocks writes
37
+
38
+ def _select(self, sql: str, params: list[Any]) -> list[dict]:
39
+ if not self._db.exists():
40
+ return []
41
+ try:
42
+ conn = self._ro()
43
+ except sqlite3.OperationalError:
44
+ return []
45
+ try:
46
+ return [dict(r) for r in conn.execute(sql, params).fetchall()]
47
+ except sqlite3.OperationalError:
48
+ return [] # table absent on a partial db
49
+ finally:
50
+ conn.close()
51
+
52
+ def events(self, *, type: str | None = None, category: str | None = None,
53
+ since: str | None = None, limit: int = 50) -> list[dict]:
54
+ conds, params = [], []
55
+ if type:
56
+ conds.append("type = ?")
57
+ params.append(type)
58
+ if category:
59
+ conds.append("category = ?")
60
+ params.append(category)
61
+ if since:
62
+ conds.append("timestamp >= ?") # shares fate w/ event_bus.query
63
+ params.append(since)
64
+ where = " AND ".join(conds) if conds else "1=1"
65
+ rows = self._select(
66
+ "SELECT id, type, category, data, priority, data_hash, project_dir,"
67
+ " attribution_confidence, timestamp, is_duplicate FROM events"
68
+ f" WHERE {where} ORDER BY timestamp DESC LIMIT ?",
69
+ params + [clamp_int(limit, 1, 500)])
70
+ for r in rows:
71
+ try:
72
+ r["data"] = json.loads(r.get("data") or "{}")
73
+ except (ValueError, TypeError):
74
+ r["data"] = {}
75
+ r["is_duplicate"] = bool(r.get("is_duplicate"))
76
+ return rows
77
+
78
+ def actions(self, *, status: str | None = None, limit: int = 50) -> list[dict]:
79
+ if status:
80
+ return self._select(
81
+ "SELECT * FROM actions WHERE status = ? ORDER BY id DESC LIMIT ?",
82
+ [status, clamp_int(limit, 1, 500)])
83
+ return self._select(
84
+ "SELECT * FROM actions ORDER BY id DESC LIMIT ?",
85
+ [clamp_int(limit, 1, 500)])
86
+
87
+ def skills(self, *, limit: int = 100) -> list[dict]:
88
+ return self._select(
89
+ "SELECT * FROM skills ORDER BY id DESC LIMIT ?",
90
+ [clamp_int(limit, 1, 500)])
91
+
92
+ # ── JSON state files ──
93
+ def goals(self) -> list[dict]:
94
+ return _read_json_list(self.storage / "goals.json") # NOT safe_read_json
95
+
96
+ def state(self) -> dict:
97
+ return safe_read_json(self.storage / "state_summary.json") or {}