dialectical-framework 1.7.0__tar.gz → 1.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/PKG-INFO +1 -1
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/pyproject.toml +1 -1
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/advisor.py +18 -7
- dialectical_framework-1.7.1/src/dialectical_framework/agents/advisor/tools/record_decision.py +85 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/tools/scoped.py +6 -3
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/analyst.py +3 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/system_prompts.py +11 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/explorer.py +3 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +1 -1
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +9 -1
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/dialectical_context.py +15 -4
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/record_decision.py +19 -4
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/dialectical_reasoning.py +14 -2
- dialectical_framework-1.7.1/src/dialectical_framework/exceptions/node_errors.py +27 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/decision.py +6 -3
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationship_manager.py +7 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/cycle_repository.py +3 -1
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/nexus_repository.py +1 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/wheel_repository.py +1 -1
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/scope_context.py +27 -0
- dialectical_framework-1.7.0/src/dialectical_framework/agents/advisor/tools/record_decision.py +0 -62
- dialectical_framework-1.7.0/src/dialectical_framework/exceptions/node_errors.py +0 -10
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/LICENSE +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/PATENTS +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/README.md +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/tools/deepen.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/agent_context.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/app_spec.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/apps.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/conversation_facilitator.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/execution_report.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/create_dx_input.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/stream_events.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/agents/toolsets.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/action_extraction.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/add_input.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/antithetical_thesis_detection.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/create_nexus.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/decision_coherence_check.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/discard.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/source_digest.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/statement_classification.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/statement_headline.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/statement_placement.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/enums/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/enums/causality_preset.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/enums/di.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/events/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/events/graph_event.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/events/graph_event_bus.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/exceptions/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/estimation_manager.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/case.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/input.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/statement.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/transition.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/grounded_in_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/rendering.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/decision_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/wheel_segment.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/protocols/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/protocols/has_config.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/protocols/input_resolver.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/settings.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/__init__.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/bedrock_provider.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/concurrency.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/dc_replace.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/edge_context.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/effect_logger.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/input_context.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/order_transitions.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/sequence_generation.py +0 -0
- {dialectical_framework-1.7.0 → dialectical_framework-1.7.1}/src/dialectical_framework/utils/use_brain.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: dialectical-framework
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.1
|
|
4
4
|
Summary: A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.).
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: dialectics,dialectical-reasoning,synthesis,thesis-antithesis,ai,artificial-intelligence,llm,reasoning-framework,philosophy,logic,argumentation,conflict-resolution,decision-making,critical-thinking,semantic-graph,mirascope,pydantic,perspectives,polarity-reasoning
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dialectical-framework"
|
|
3
|
-
version = "1.7.
|
|
3
|
+
version = "1.7.1"
|
|
4
4
|
description = "A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.)."
|
|
5
5
|
authors = ["Evaldas Taroza <evaldas@dialexity.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -20,6 +20,7 @@ from pydantic import BaseModel, Field
|
|
|
20
20
|
from dialectical_framework.agents.advisor.system_prompts import \
|
|
21
21
|
system_prompt
|
|
22
22
|
from dialectical_framework.agents.agent_context import agent_scope
|
|
23
|
+
from dialectical_framework.graph.scope_context import require_current_sid
|
|
23
24
|
from dialectical_framework.agents.conversation_facilitator import \
|
|
24
25
|
ConversationFacilitator
|
|
25
26
|
from dialectical_framework.agents.app_spec import AppSpec, resolve_app_layer
|
|
@@ -120,7 +121,15 @@ class Advisor:
|
|
|
120
121
|
nexus_hash: Optional[str] = None,
|
|
121
122
|
app_tools: Optional[list] = None,
|
|
122
123
|
app: Optional[AppSpec] = None,
|
|
124
|
+
principal: str = "human",
|
|
123
125
|
) -> None:
|
|
126
|
+
# principal: WHO confirms decisions in this conversation — a host
|
|
127
|
+
# attestation, fixed for the session (the counterpart doesn't change
|
|
128
|
+
# mid-conversation). "human" = an actual person; a delegated driver
|
|
129
|
+
# (agent-to-agent runs) must pass its own identity ("agent:<name>"
|
|
130
|
+
# or <provider>/<model>) so recorded decisions never claim human
|
|
131
|
+
# confirmation they didn't get. Closed over by record_decision —
|
|
132
|
+
# never an LLM-visible parameter.
|
|
124
133
|
self._nexus_hash = nexus_hash
|
|
125
134
|
# app: declarative app definition — composition depends on the mode:
|
|
126
135
|
# counsel toggle (nexus_hash set) keeps the Navigator contract
|
|
@@ -134,9 +143,9 @@ class Advisor:
|
|
|
134
143
|
)
|
|
135
144
|
if nexus_hash:
|
|
136
145
|
self._validate_nexus(nexus_hash)
|
|
137
|
-
self._tools = _build_scoped_tools(nexus_hash)
|
|
146
|
+
self._tools = _build_scoped_tools(nexus_hash, principal)
|
|
138
147
|
else:
|
|
139
|
-
self._tools = _build_tools()
|
|
148
|
+
self._tools = _build_tools(principal)
|
|
140
149
|
# App-provided @llm.tool functions (domain resources: chart lookups,
|
|
141
150
|
# methodology references, ...) — see toolsets.merge_app_tools.
|
|
142
151
|
self._tools = merge_app_tools(self._tools, app_tools)
|
|
@@ -185,12 +194,14 @@ class Advisor:
|
|
|
185
194
|
return "\n\n".join(parts)
|
|
186
195
|
|
|
187
196
|
async def chat(self, user_message: str) -> str:
|
|
197
|
+
require_current_sid() # unscoped turns silently drop all work
|
|
188
198
|
with agent_scope(self.AGENT_NAME):
|
|
189
199
|
await self._render_pending_context()
|
|
190
200
|
result = await self._conversation.submit(ChatResponse, user_message)
|
|
191
201
|
return result.message
|
|
192
202
|
|
|
193
203
|
async def chat_stream(self, user_message: str) -> AsyncGenerator[StreamEvent, None]:
|
|
204
|
+
require_current_sid() # unscoped turns silently drop all work
|
|
194
205
|
with agent_scope(self.AGENT_NAME):
|
|
195
206
|
await self._render_pending_context()
|
|
196
207
|
async for event in self._conversation.submit_stream(
|
|
@@ -233,13 +244,13 @@ class Advisor:
|
|
|
233
244
|
return self._conversation._messages
|
|
234
245
|
|
|
235
246
|
|
|
236
|
-
def _build_tools() -> list:
|
|
247
|
+
def _build_tools(principal: str = "human") -> list:
|
|
237
248
|
from dialectical_framework.agents.advisor.tools.anchor import anchor
|
|
238
249
|
from dialectical_framework.agents.advisor.tools.deepen import deepen
|
|
239
250
|
from dialectical_framework.agents.advisor.tools.explore import explore
|
|
240
251
|
from dialectical_framework.agents.advisor.tools.ingest import ingest
|
|
241
252
|
from dialectical_framework.agents.advisor.tools.record_decision import \
|
|
242
|
-
|
|
253
|
+
build_record_decision
|
|
243
254
|
from dialectical_framework.agents.advisor.tools.sync import sync
|
|
244
255
|
from dialectical_framework.agents.orchestrator.tools.inspect_node import \
|
|
245
256
|
inspect_node
|
|
@@ -253,7 +264,7 @@ def _build_tools() -> list:
|
|
|
253
264
|
anchor,
|
|
254
265
|
explore,
|
|
255
266
|
deepen,
|
|
256
|
-
|
|
267
|
+
build_record_decision(principal),
|
|
257
268
|
sync,
|
|
258
269
|
inspect_node,
|
|
259
270
|
read_digest,
|
|
@@ -261,8 +272,8 @@ def _build_tools() -> list:
|
|
|
261
272
|
]
|
|
262
273
|
|
|
263
274
|
|
|
264
|
-
def _build_scoped_tools(nexus_hash: str) -> list:
|
|
275
|
+
def _build_scoped_tools(nexus_hash: str, principal: str = "human") -> list:
|
|
265
276
|
from dialectical_framework.agents.advisor.tools.scoped import \
|
|
266
277
|
build_scoped_tools
|
|
267
278
|
|
|
268
|
-
return build_scoped_tools(nexus_hash)
|
|
279
|
+
return build_scoped_tools(nexus_hash, principal)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
record_decision tool: persist a decision the person explicitly confirmed.
|
|
3
|
+
|
|
4
|
+
The ceremony lives in conversation (propose → read back → confirm) — this
|
|
5
|
+
tool is called only AFTER the person's explicit yes, with the confirmed
|
|
6
|
+
wording passed literally. Retracting or replacing a recorded decision goes
|
|
7
|
+
through the standard `discard` tool (reason referencing the new decision).
|
|
8
|
+
|
|
9
|
+
Provenance: WHO confirmed is a host-attested fact, closed over at tool
|
|
10
|
+
construction (Advisor(principal=...)) — never an LLM-supplied parameter.
|
|
11
|
+
"human" (the default) means an actual person confirmed the wording; a
|
|
12
|
+
delegated driver (agent-to-agent runs) must be constructed with its own
|
|
13
|
+
identity, else the ledger and inspect_node would present machine-authored
|
|
14
|
+
rationales as the person's own confirmed "why".
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import Annotated
|
|
20
|
+
|
|
21
|
+
from mirascope import llm
|
|
22
|
+
from pydantic import Field
|
|
23
|
+
|
|
24
|
+
from dialectical_framework.concerns.record_decision import GroundLink
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def build_record_decision(principal: str = "human"):
|
|
28
|
+
"""Build the record_decision tool with the confirming principal
|
|
29
|
+
closed over (same code-not-prompt enforcement as the nexus pin in
|
|
30
|
+
scoped.py)."""
|
|
31
|
+
|
|
32
|
+
attested_principal = principal
|
|
33
|
+
|
|
34
|
+
@llm.tool
|
|
35
|
+
async def record_decision(
|
|
36
|
+
question: Annotated[
|
|
37
|
+
str,
|
|
38
|
+
Field(description="The question that was being decided, in the person's confirmed words"),
|
|
39
|
+
],
|
|
40
|
+
stance: Annotated[
|
|
41
|
+
str,
|
|
42
|
+
Field(description="The stance the person committed to, in their confirmed words"),
|
|
43
|
+
],
|
|
44
|
+
rationale: Annotated[
|
|
45
|
+
str,
|
|
46
|
+
Field(
|
|
47
|
+
description="The distilled why behind the decision as the person confirmed it, "
|
|
48
|
+
"including reasons that live outside the mapped structure (deadlines, "
|
|
49
|
+
"people, external facts)"
|
|
50
|
+
),
|
|
51
|
+
],
|
|
52
|
+
grounds: Annotated[
|
|
53
|
+
list[GroundLink] | None,
|
|
54
|
+
Field(
|
|
55
|
+
description="Nodes this decision rests on: {hash, role?} each. "
|
|
56
|
+
"role='accepted_cost' for the unchosen side's contribution the person "
|
|
57
|
+
"confronted and accepted; role='adopted_pathway' for the pathway adopted "
|
|
58
|
+
"as the ongoing recipe; omit role for a plain ground (tensions weighed, "
|
|
59
|
+
"arrangements counseled from)."
|
|
60
|
+
),
|
|
61
|
+
] = None,
|
|
62
|
+
) -> str:
|
|
63
|
+
"""Record a decision the person has EXPLICITLY confirmed in conversation. Never call this silently or speculatively — propose the distilled record first, read it back, and record only on their clear yes. Links the decision to the graph nodes that grounded it and attaches the confirmed rationale. To replace or retract a recorded decision, use `discard` on it (with a reason naming the newer decision if one replaces it)."""
|
|
64
|
+
from dialectical_framework.concerns.record_decision import RecordDecision
|
|
65
|
+
|
|
66
|
+
# Mirascope passes json.loads'd kwargs without coercing nested models, so
|
|
67
|
+
# `grounds` arrives as raw dicts — RecordDecision.resolve normalizes them
|
|
68
|
+
# (single owner of that normalization) and refuses in-band on bad input.
|
|
69
|
+
concern = RecordDecision()
|
|
70
|
+
await concern.resolve(
|
|
71
|
+
question=question,
|
|
72
|
+
stance=stance,
|
|
73
|
+
rationale=rationale,
|
|
74
|
+
grounds=grounds,
|
|
75
|
+
principal=attested_principal,
|
|
76
|
+
)
|
|
77
|
+
return str(concern.report)
|
|
78
|
+
|
|
79
|
+
return record_decision
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Default build: a human principal. Kept module-level so existing imports
|
|
83
|
+
# (toolset builders, tests) stay valid; hosts with a non-human driver pass
|
|
84
|
+
# principal= at Advisor construction instead of importing this.
|
|
85
|
+
record_decision = build_record_decision()
|
|
@@ -32,23 +32,26 @@ from mirascope import llm
|
|
|
32
32
|
from pydantic import Field
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def build_scoped_tools(nexus_hash: str) -> list:
|
|
35
|
+
def build_scoped_tools(nexus_hash: str, principal: str = "human") -> list:
|
|
36
36
|
"""
|
|
37
37
|
Build the tool set for an exploration-pinned Advisor (counsel mode).
|
|
38
38
|
|
|
39
39
|
The nexus_hash is captured by closure — the LLM cannot redirect any of
|
|
40
40
|
these tools to another nexus or create a new one. Validation that the
|
|
41
41
|
nexus exists happens in Advisor.__init__, not here (keeps the factory
|
|
42
|
-
DB-free for signature tests).
|
|
42
|
+
DB-free for signature tests). `principal` (host-attested confirmer
|
|
43
|
+
identity, closed over the same way) reaches record_decision — see
|
|
44
|
+
tools/record_decision.py.
|
|
43
45
|
"""
|
|
44
46
|
from dialectical_framework.agents.advisor.tools.anchor import anchor
|
|
45
47
|
from dialectical_framework.agents.advisor.tools.record_decision import \
|
|
46
|
-
|
|
48
|
+
build_record_decision
|
|
47
49
|
from dialectical_framework.agents.orchestrator.tools.inspect_node import \
|
|
48
50
|
inspect_node
|
|
49
51
|
from dialectical_framework.agents.orchestrator.tools.read_digest import \
|
|
50
52
|
read_digest
|
|
51
53
|
|
|
54
|
+
record_decision = build_record_decision(principal)
|
|
52
55
|
pinned_hash = nexus_hash
|
|
53
56
|
|
|
54
57
|
@llm.tool
|
|
@@ -16,6 +16,7 @@ from mirascope import llm
|
|
|
16
16
|
from pydantic import BaseModel, Field
|
|
17
17
|
|
|
18
18
|
from dialectical_framework.agents.agent_context import agent_scope
|
|
19
|
+
from dialectical_framework.graph.scope_context import require_current_sid
|
|
19
20
|
from dialectical_framework.agents.analyst.system_prompts import SYSTEM_PROMPT
|
|
20
21
|
from dialectical_framework.agents.conversation_facilitator import \
|
|
21
22
|
ConversationFacilitator
|
|
@@ -93,11 +94,13 @@ class Analyst:
|
|
|
93
94
|
return "\n\n".join(parts)
|
|
94
95
|
|
|
95
96
|
async def chat(self, user_message: str) -> str:
|
|
97
|
+
require_current_sid() # unscoped turns silently drop all work
|
|
96
98
|
with agent_scope(self.AGENT_NAME):
|
|
97
99
|
result = await self._conversation.submit(ChatResponse, user_message)
|
|
98
100
|
return result.message
|
|
99
101
|
|
|
100
102
|
async def chat_stream(self, user_message: str) -> AsyncGenerator[StreamEvent, None]:
|
|
103
|
+
require_current_sid() # unscoped turns silently drop all work
|
|
101
104
|
with agent_scope(self.AGENT_NAME):
|
|
102
105
|
async for event in self._conversation.submit_stream(
|
|
103
106
|
ChatResponse, user_message
|
|
@@ -67,6 +67,17 @@ Always check resonance AFTER presenting results — but never before acting.
|
|
|
67
67
|
When new tensions emerge from conversation:
|
|
68
68
|
- Call `analyze` with `thesis_hashes` to develop them without re-processing everything.
|
|
69
69
|
|
|
70
|
+
**When the user declares a decision** ("that settles it, I'm going with X")
|
|
71
|
+
or asks to have a decision noted or recorded: you cannot record decisions —
|
|
72
|
+
that happens in the advisory conversation, not here. NEVER claim to have
|
|
73
|
+
noted a decision yourself, and NEVER anchor the declared choice as a thesis —
|
|
74
|
+
a decision is a stance the user takes, not a tension to analyze; persisting
|
|
75
|
+
it as a statement would misfile it in the analytical structure. Acknowledge
|
|
76
|
+
their choice plainly and continue the structural work; if the application
|
|
77
|
+
offers an advisory conversation, point them there for recording. Decisions
|
|
78
|
+
already recorded are readable: they appear via `query_graph` (Decision
|
|
79
|
+
nodes) and `inspect_node` by hash.
|
|
80
|
+
|
|
70
81
|
**Inputs from exploration (`dx://` URIs) — the round-trip.** An input whose
|
|
71
82
|
content starts with `dx://` is an exploration insight fed back for analysis:
|
|
72
83
|
it references a Transition (an action/reflection pathway) whose wisdom
|
|
@@ -16,6 +16,7 @@ from mirascope import llm
|
|
|
16
16
|
from pydantic import BaseModel, Field
|
|
17
17
|
|
|
18
18
|
from dialectical_framework.agents.agent_context import agent_scope
|
|
19
|
+
from dialectical_framework.graph.scope_context import require_current_sid
|
|
19
20
|
from dialectical_framework.agents.conversation_facilitator import \
|
|
20
21
|
ConversationFacilitator
|
|
21
22
|
from dialectical_framework.agents.explorer.system_prompts import system_prompt
|
|
@@ -109,11 +110,13 @@ class Explorer:
|
|
|
109
110
|
return "\n\n".join(parts)
|
|
110
111
|
|
|
111
112
|
async def chat(self, user_message: str) -> str:
|
|
113
|
+
require_current_sid() # unscoped turns silently drop all work
|
|
112
114
|
with agent_scope(self.AGENT_NAME):
|
|
113
115
|
result = await self._conversation.submit(ChatResponse, user_message)
|
|
114
116
|
return result.message
|
|
115
117
|
|
|
116
118
|
async def chat_stream(self, user_message: str) -> AsyncGenerator[StreamEvent, None]:
|
|
119
|
+
require_current_sid() # unscoped turns silently drop all work
|
|
117
120
|
with agent_scope(self.AGENT_NAME):
|
|
118
121
|
async for event in self._conversation.submit_stream(
|
|
119
122
|
ChatResponse, user_message
|
|
@@ -31,7 +31,7 @@ GRAPH_SCHEMA = """## Graph Schema
|
|
|
31
31
|
| Transformation | Action-reflection structure (Ac, Re, Ac+, Ac-, Re+, Re-) on a Wheel edge | `intent` |
|
|
32
32
|
| Transition | Movement between two Statements (source → target) | `statement`, `headline`, `haiku` |
|
|
33
33
|
| Synthesis | Emergent S+/S- pair from a Wheel's circular causality | |
|
|
34
|
-
| Decision |
|
|
34
|
+
| Decision | Confirmed decision: question + stance (Case-level, immutable; timestamp = `committed_at`; the why = attached Rationale whose `agent` names the confirming principal — "human" for a person, "agent:<name>" for a delegated driver) | `intent` (the question), `stance`, `discarded`, `validation` ("passed" / "failed: ..." / null=unchecked) |
|
|
35
35
|
| Rationale | Explanation attached to any node | `text` |
|
|
36
36
|
| Estimation | Numeric assessment (probability, relevance, feasibility) | `value` |
|
|
37
37
|
|
|
@@ -288,7 +288,15 @@ def _inspect_decision(decision: Decision) -> str:
|
|
|
288
288
|
lines.append(f"Stance: {decision.stance}")
|
|
289
289
|
|
|
290
290
|
for rationale, _ in decision.rationales.all():
|
|
291
|
-
|
|
291
|
+
# "human" = a person confirmed the ceremony; "agent:<name>" = a
|
|
292
|
+
# delegated driver confirmed (still the ceremony why, attributed);
|
|
293
|
+
# anything else = machine-generated analysis (critique etc.).
|
|
294
|
+
if rationale.agent == "human":
|
|
295
|
+
prefix = "Why (human)"
|
|
296
|
+
elif rationale.agent and rationale.agent.startswith("agent:"):
|
|
297
|
+
prefix = f"Why (confirmed by {rationale.agent})"
|
|
298
|
+
else:
|
|
299
|
+
prefix = f"Rationale ({rationale.agent})"
|
|
292
300
|
lines.append(f"{prefix}: {rationale.text}")
|
|
293
301
|
|
|
294
302
|
grounds = decision.grounds.all()
|
|
@@ -252,12 +252,20 @@ class DialecticalContext(ReasonableConcern[str], SettingsAware):
|
|
|
252
252
|
# raw they could fabricate sibling ledger lines (injection).
|
|
253
253
|
lines.append(f"Question: {one_line(d.intent)}")
|
|
254
254
|
lines.append(f"Stance: {one_line(d.stance)}")
|
|
255
|
-
# Ledger shows only the
|
|
255
|
+
# Ledger shows only the ceremony-confirmed why; machine rationales
|
|
256
256
|
# (critiques etc.) stay one inspect_node away — deliberate
|
|
257
|
-
# divergence from _inspect_decision, which shows all.
|
|
257
|
+
# divergence from _inspect_decision, which shows all. "human" =
|
|
258
|
+
# a person confirmed; "agent:<name>" = a delegated driver
|
|
259
|
+
# confirmed (rendered with attribution so a human reading the
|
|
260
|
+
# ledger never mistakes it for their own confirmation).
|
|
258
261
|
for rationale, _ in d.rationales.all():
|
|
259
262
|
if rationale.agent == "human":
|
|
260
263
|
lines.append(f"Why: {one_line(rationale.text)}")
|
|
264
|
+
elif rationale.agent and rationale.agent.startswith("agent:"):
|
|
265
|
+
lines.append(
|
|
266
|
+
f"Why (confirmed by {rationale.agent}): "
|
|
267
|
+
f"{one_line(rationale.text)}"
|
|
268
|
+
)
|
|
261
269
|
if d.validation:
|
|
262
270
|
lines.append(f"Validation: {one_line(d.validation)}")
|
|
263
271
|
grounds = d.grounds.all()
|
|
@@ -514,10 +522,13 @@ class DialecticalContext(ReasonableConcern[str], SettingsAware):
|
|
|
514
522
|
total_wheel_prob = sum(p for p in wheel_probs.values() if p is not None)
|
|
515
523
|
|
|
516
524
|
max_wheels = 0 if self._nexus_hash else self.settings.advisor_wheel_quality_top_plausible
|
|
525
|
+
# Hash tiebreaker: with probability-only keys, tied (or unscored)
|
|
526
|
+
# wheels resolve by arrival order — unspecified in Cypher — so
|
|
527
|
+
# which wheel makes the rendered top-N could differ between two
|
|
528
|
+
# renders of the SAME graph. The hash pins it.
|
|
517
529
|
rendered = sorted(
|
|
518
530
|
wheels,
|
|
519
|
-
key=lambda w: wheel_probs.get(w._id) or -1.0,
|
|
520
|
-
reverse=True,
|
|
531
|
+
key=lambda w: (-(wheel_probs.get(w._id) or -1.0), w.hash or ""),
|
|
521
532
|
)
|
|
522
533
|
hidden = 0
|
|
523
534
|
if max_wheels > 0 and len(rendered) > max_wheels:
|
|
@@ -9,7 +9,9 @@ confirmed wording, never as instructions to interpret. The concern:
|
|
|
9
9
|
fail-closed for grounds, since a dangling or reader-invisible ground
|
|
10
10
|
would break the record's value)
|
|
11
11
|
2. Commits the Decision (question rides intent, stance frozen in hash)
|
|
12
|
-
3. Attaches the distilled why as a Rationale
|
|
12
|
+
3. Attaches the distilled why as a Rationale carrying the confirming
|
|
13
|
+
principal's provenance (agent="human" only when a person confirmed;
|
|
14
|
+
delegated drivers pass their own identity — see resolve())
|
|
13
15
|
4. Connects GROUNDED_IN edges with their roles
|
|
14
16
|
5. Runs DecisionCoherenceCheck and flags the verdict (fail-soft: a failed
|
|
15
17
|
or errored check NEVER blocks the record — soft gate)
|
|
@@ -62,7 +64,19 @@ class RecordDecision(ReasonableConcern[str | None]):
|
|
|
62
64
|
stance: str,
|
|
63
65
|
rationale: str,
|
|
64
66
|
grounds: list[GroundLink] | None = None,
|
|
67
|
+
principal: str = "human",
|
|
65
68
|
) -> str | None:
|
|
69
|
+
"""
|
|
70
|
+
`principal` is the provenance stamped on the decision's Rationale —
|
|
71
|
+
WHO confirmed the recording ceremony. "human" (default) is the
|
|
72
|
+
sentinel meaning an actual person confirmed the wording; a delegated
|
|
73
|
+
driver (agent-to-agent runs) must pass its own identity (e.g.
|
|
74
|
+
"agent:<name>" or a <provider>/<model> string) — the ledger and
|
|
75
|
+
inspect_node render only human-confirmed rationales as the person's
|
|
76
|
+
own "why", so a false "human" here corrupts the record's authority
|
|
77
|
+
semantics unfixably. Attested by the HOST at agent construction
|
|
78
|
+
(Advisor(principal=...)), never by the LLM.
|
|
79
|
+
"""
|
|
66
80
|
from dialectical_framework.concerns.decision_coherence_check import \
|
|
67
81
|
DecisionCoherenceCheck
|
|
68
82
|
from dialectical_framework.graph.nodes.assessable_entity import \
|
|
@@ -170,12 +184,13 @@ class RecordDecision(ReasonableConcern[str | None]):
|
|
|
170
184
|
# a raw exception hiding that a Decision now exists.
|
|
171
185
|
attach_failures: list[str] = []
|
|
172
186
|
try:
|
|
173
|
-
# The distilled why: a
|
|
187
|
+
# The distilled why: a Rationale carrying the confirming
|
|
188
|
+
# principal's provenance (see resolve() docstring).
|
|
174
189
|
why = Rationale(text=rationale)
|
|
175
|
-
why.agent =
|
|
190
|
+
why.agent = principal # overwrite the auto-filled model identifier
|
|
176
191
|
why.set_explanation_target(decision)
|
|
177
192
|
why.commit() # auto-connects EXPLAINS
|
|
178
|
-
self._report.node_created(why, patch={"agent":
|
|
193
|
+
self._report.node_created(why, patch={"agent": principal})
|
|
179
194
|
self._report.relationship_created(why.explains, why, decision)
|
|
180
195
|
except Exception as e:
|
|
181
196
|
attach_failures.append(f"rationale not attached ({e})")
|
|
@@ -166,8 +166,20 @@ class DialecticalReasoning(containers.DeclarativeContainer):
|
|
|
166
166
|
graph_db.execute(
|
|
167
167
|
"CREATE CONSTRAINT ON (n:Node) ASSERT n.hash, n.sid IS UNIQUE"
|
|
168
168
|
)
|
|
169
|
-
except Exception:
|
|
170
|
-
|
|
169
|
+
except Exception as e:
|
|
170
|
+
# Constraint may already exist or DB doesn't support it —
|
|
171
|
+
# but say so: this constraint is the only backstop against
|
|
172
|
+
# commit()'s check-then-act dedup race producing duplicate
|
|
173
|
+
# committed nodes under concurrent writers. Silent absence
|
|
174
|
+
# turns that race into silent corruption.
|
|
175
|
+
import logging
|
|
176
|
+
|
|
177
|
+
logging.getLogger(__name__).warning(
|
|
178
|
+
"Could not create (hash, sid) uniqueness constraint: %s — "
|
|
179
|
+
"duplicate-node protection is NOT active; ensure a single "
|
|
180
|
+
"writer per sid.",
|
|
181
|
+
e,
|
|
182
|
+
)
|
|
171
183
|
|
|
172
184
|
# Graph database (Memgraph or Neo4j) for graph-native dialectical structures
|
|
173
185
|
graph_db: providers.Singleton[Union[Memgraph, Neo4j]] = providers.Singleton(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Node-related exceptions for the dialectical framework.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ImmutableNodeError(Exception):
|
|
9
|
+
"""Raised when attempting to modify a committed (immutable) node's structure."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MissingScopeError(RuntimeError):
|
|
13
|
+
"""Raised when an agent turn starts with no scope (sid) set.
|
|
14
|
+
|
|
15
|
+
Agent conversations (Analyst/Explorer/Advisor chat) write sid-scoped
|
|
16
|
+
nodes through their tools. Without `with scope(case.sid):` the turn
|
|
17
|
+
would fail SILENTLY: nodes save with sid=None, invisible to every
|
|
18
|
+
repository listing (work dropped while tool reports claim success),
|
|
19
|
+
and commit-time dedup falls back to an UNSCOPED find_by_hash that can
|
|
20
|
+
alias a node onto another Case's node, attaching foreign edges to it.
|
|
21
|
+
|
|
22
|
+
Deliberately an agent-level guard, not a BaseNode one: sid-less
|
|
23
|
+
"orphan" nodes are a supported node-layer semantic (portable
|
|
24
|
+
identifiers — see TestOrphanNodes), so programmatic callers may
|
|
25
|
+
create them; conversational turns never legitimately run unscoped.
|
|
26
|
+
"""
|
|
27
|
+
|
|
@@ -32,9 +32,12 @@ class Decision(IntentMixin, AssessableEntity, label="Decision"):
|
|
|
32
32
|
Thin by design — the record points at the reasoning rather than
|
|
33
33
|
restating it:
|
|
34
34
|
- The question rides `intent` (IntentMixin); REQUIRED at commit.
|
|
35
|
-
- The distilled why is a Rationale
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
- The distilled why is a Rationale attached via the standard EXPLAINS
|
|
36
|
+
machinery (inherited `rationales`), which also buys ratings/
|
|
37
|
+
estimations/critiques on decisions. Its `agent` names the confirming
|
|
38
|
+
principal: "human" iff a person confirmed the ceremony; a delegated
|
|
39
|
+
driver records "agent:<name>" (host-attested via
|
|
40
|
+
Advisor(principal=...), never LLM-supplied).
|
|
38
41
|
- Grounds are GROUNDED_IN edges to committed nodes (perspectives,
|
|
39
42
|
statements, wheels, transformations) with an optional role
|
|
40
43
|
("accepted_cost", "adopted_pathway", or None for a plain ground).
|
|
@@ -1162,10 +1162,17 @@ class BoundRelationshipManager(Generic[T]):
|
|
|
1162
1162
|
else: # any
|
|
1163
1163
|
pattern = f"(source)-[r:{self.relationship_type}]-(target:{resolved_labels})"
|
|
1164
1164
|
|
|
1165
|
+
# Deterministic ordering: without ORDER BY, Cypher result order is
|
|
1166
|
+
# unspecified (stable only by accident of current storage) — but
|
|
1167
|
+
# consumers like build_pp_index treat this ordering as canonical
|
|
1168
|
+
# (T1/T2 indices, cycle sequences). committed_at gives temporal
|
|
1169
|
+
# order; id(target) tiebreaks same-second commits and uncommitted
|
|
1170
|
+
# targets (NULL committed_at sorts consistently within a vendor).
|
|
1165
1171
|
query = f"""
|
|
1166
1172
|
MATCH {pattern}
|
|
1167
1173
|
WHERE id(source) = $source_id
|
|
1168
1174
|
RETURN target, r as relationship
|
|
1175
|
+
ORDER BY target.committed_at ASC, id(target) ASC
|
|
1169
1176
|
"""
|
|
1170
1177
|
|
|
1171
1178
|
results = db.execute_and_fetch(query, {"source_id": self.source_node._id})
|
|
@@ -83,6 +83,7 @@ class CycleRepository:
|
|
|
83
83
|
AND size(c.perspective_hashes) = $hash_count
|
|
84
84
|
AND ALL(h IN $pp_hashes WHERE h IN c.perspective_hashes)
|
|
85
85
|
RETURN c
|
|
86
|
+
ORDER BY c.committed_at ASC, id(c) ASC
|
|
86
87
|
"""
|
|
87
88
|
results = list(graph_db.execute_and_fetch(query, {
|
|
88
89
|
"sid": sid,
|
|
@@ -107,6 +108,7 @@ class CycleRepository:
|
|
|
107
108
|
WHERE c.sid = $sid
|
|
108
109
|
AND ANY(h IN $pp_hashes WHERE h IN c.perspective_hashes)
|
|
109
110
|
RETURN c
|
|
111
|
+
ORDER BY c.committed_at ASC, id(c) ASC
|
|
110
112
|
"""
|
|
111
113
|
results = list(graph_db.execute_and_fetch(query, {
|
|
112
114
|
"sid": sid,
|
|
@@ -165,7 +167,7 @@ class CycleRepository:
|
|
|
165
167
|
query += " AND ALL(h IN c.perspective_hashes WHERE h IN $nexus_pp_hashes)\n"
|
|
166
168
|
params["nexus_pp_hashes"] = nexus_pp_hashes
|
|
167
169
|
|
|
168
|
-
query += " RETURN c"
|
|
170
|
+
query += " RETURN c\n ORDER BY c.committed_at ASC, id(c) ASC"
|
|
169
171
|
|
|
170
172
|
results = list(graph_db.execute_and_fetch(query, params))
|
|
171
173
|
|
|
@@ -197,7 +197,7 @@ class WheelRepository:
|
|
|
197
197
|
query += " AND ALL(h IN c.perspective_hashes WHERE h IN $nexus_pp_hashes)\n"
|
|
198
198
|
params["nexus_pp_hashes"] = nexus_pp_hashes
|
|
199
199
|
|
|
200
|
-
query += " RETURN w"
|
|
200
|
+
query += " RETURN w\n ORDER BY w.committed_at ASC, id(w) ASC"
|
|
201
201
|
|
|
202
202
|
results = list(graph_db.execute_and_fetch(query, params))
|
|
203
203
|
|
|
@@ -33,6 +33,33 @@ _current_scope: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar(
|
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def require_current_sid() -> str:
|
|
37
|
+
"""
|
|
38
|
+
Get the current scope ID (sid), raising if none is set.
|
|
39
|
+
|
|
40
|
+
Guard for entry points that must never run unscoped (agent chat
|
|
41
|
+
turns): running without scope fails silently otherwise — nodes save
|
|
42
|
+
with sid=None (invisible to every sid-scoped listing) and commit-time
|
|
43
|
+
dedup falls back to an unscoped lookup that can alias onto another
|
|
44
|
+
Case's nodes. Fail loud at the boundary instead.
|
|
45
|
+
|
|
46
|
+
Note: this is NOT enforced at the node layer — sid-less orphan nodes
|
|
47
|
+
are a supported semantic for programmatic callers (portable
|
|
48
|
+
identifiers). Only conversational entry points call this.
|
|
49
|
+
"""
|
|
50
|
+
from dialectical_framework.exceptions.node_errors import \
|
|
51
|
+
MissingScopeError
|
|
52
|
+
|
|
53
|
+
sid = _current_scope.get()
|
|
54
|
+
if not sid:
|
|
55
|
+
raise MissingScopeError(
|
|
56
|
+
"No scope set for this agent turn. Wrap the call in "
|
|
57
|
+
"`with scope(case.sid):` — the framework never sets scope "
|
|
58
|
+
"itself, and running unscoped would silently drop all work."
|
|
59
|
+
)
|
|
60
|
+
return sid
|
|
61
|
+
|
|
62
|
+
|
|
36
63
|
def get_current_sid() -> Optional[str]:
|
|
37
64
|
"""
|
|
38
65
|
Get the current scope ID (sid) from context.
|
dialectical_framework-1.7.0/src/dialectical_framework/agents/advisor/tools/record_decision.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
record_decision tool: persist a decision the person explicitly confirmed.
|
|
3
|
-
|
|
4
|
-
The ceremony lives in conversation (propose → read back → confirm) — this
|
|
5
|
-
tool is called only AFTER the person's explicit yes, with the confirmed
|
|
6
|
-
wording passed literally. Retracting or replacing a recorded decision goes
|
|
7
|
-
through the standard `discard` tool (reason referencing the new decision).
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
from typing import Annotated
|
|
13
|
-
|
|
14
|
-
from mirascope import llm
|
|
15
|
-
from pydantic import Field
|
|
16
|
-
|
|
17
|
-
from dialectical_framework.concerns.record_decision import GroundLink
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@llm.tool
|
|
21
|
-
async def record_decision(
|
|
22
|
-
question: Annotated[
|
|
23
|
-
str,
|
|
24
|
-
Field(description="The question that was being decided, in the person's confirmed words"),
|
|
25
|
-
],
|
|
26
|
-
stance: Annotated[
|
|
27
|
-
str,
|
|
28
|
-
Field(description="The stance the person committed to, in their confirmed words"),
|
|
29
|
-
],
|
|
30
|
-
rationale: Annotated[
|
|
31
|
-
str,
|
|
32
|
-
Field(
|
|
33
|
-
description="The distilled why behind the decision as the person confirmed it, "
|
|
34
|
-
"including reasons that live outside the mapped structure (deadlines, "
|
|
35
|
-
"people, external facts)"
|
|
36
|
-
),
|
|
37
|
-
],
|
|
38
|
-
grounds: Annotated[
|
|
39
|
-
list[GroundLink] | None,
|
|
40
|
-
Field(
|
|
41
|
-
description="Nodes this decision rests on: {hash, role?} each. "
|
|
42
|
-
"role='accepted_cost' for the unchosen side's contribution the person "
|
|
43
|
-
"confronted and accepted; role='adopted_pathway' for the pathway adopted "
|
|
44
|
-
"as the ongoing recipe; omit role for a plain ground (tensions weighed, "
|
|
45
|
-
"arrangements counseled from)."
|
|
46
|
-
),
|
|
47
|
-
] = None,
|
|
48
|
-
) -> str:
|
|
49
|
-
"""Record a decision the person has EXPLICITLY confirmed in conversation. Never call this silently or speculatively — propose the distilled record first, read it back, and record only on their clear yes. Links the decision to the graph nodes that grounded it and attaches the confirmed rationale. To replace or retract a recorded decision, use `discard` on it (with a reason naming the newer decision if one replaces it)."""
|
|
50
|
-
from dialectical_framework.concerns.record_decision import RecordDecision
|
|
51
|
-
|
|
52
|
-
# Mirascope passes json.loads'd kwargs without coercing nested models, so
|
|
53
|
-
# `grounds` arrives as raw dicts — RecordDecision.resolve normalizes them
|
|
54
|
-
# (single owner of that normalization) and refuses in-band on bad input.
|
|
55
|
-
concern = RecordDecision()
|
|
56
|
-
await concern.resolve(
|
|
57
|
-
question=question,
|
|
58
|
-
stance=stance,
|
|
59
|
-
rationale=rationale,
|
|
60
|
-
grounds=grounds,
|
|
61
|
-
)
|
|
62
|
-
return str(concern.report)
|
|
File without changes
|
|
File without changes
|