briefloop 0.11.12__py3-none-any.whl
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.
- briefloop-0.11.12.dist-info/METADATA +473 -0
- briefloop-0.11.12.dist-info/RECORD +424 -0
- briefloop-0.11.12.dist-info/WHEEL +5 -0
- briefloop-0.11.12.dist-info/entry_points.txt +3 -0
- briefloop-0.11.12.dist-info/licenses/LICENSE +22 -0
- briefloop-0.11.12.dist-info/top_level.txt +1 -0
- multi_agent_brief/__init__.py +36 -0
- multi_agent_brief/analysis_blocks/__init__.py +11 -0
- multi_agent_brief/analysis_blocks/builder.py +206 -0
- multi_agent_brief/analysis_blocks/renderer.py +237 -0
- multi_agent_brief/analysis_blocks/schemas.py +52 -0
- multi_agent_brief/analysis_modules/__init__.py +8 -0
- multi_agent_brief/analysis_modules/base.py +87 -0
- multi_agent_brief/analysis_modules/market_competitor/__init__.py +68 -0
- multi_agent_brief/analysis_modules/market_competitor/auditor.py +290 -0
- multi_agent_brief/analysis_modules/market_competitor/config.py +187 -0
- multi_agent_brief/analysis_modules/market_competitor/event_builder.py +226 -0
- multi_agent_brief/analysis_modules/market_competitor/renderer.py +199 -0
- multi_agent_brief/analysis_modules/market_competitor/schemas.py +518 -0
- multi_agent_brief/analysis_modules/policy_regulatory/__init__.py +7 -0
- multi_agent_brief/analysis_modules/policy_regulatory/audit.py +195 -0
- multi_agent_brief/analysis_modules/policy_regulatory/module.py +387 -0
- multi_agent_brief/analysis_modules/policy_regulatory/schemas.py +129 -0
- multi_agent_brief/analysis_modules/registry.py +73 -0
- multi_agent_brief/audience/__init__.py +19 -0
- multi_agent_brief/audience/profiles.py +246 -0
- multi_agent_brief/audience_memory/__init__.py +23 -0
- multi_agent_brief/audience_memory/profile.py +358 -0
- multi_agent_brief/audit/__init__.py +36 -0
- multi_agent_brief/audit/case_applicability.py +181 -0
- multi_agent_brief/audit/deterministic.py +471 -0
- multi_agent_brief/audit/editorial_governance.py +404 -0
- multi_agent_brief/audit/final_quality.py +764 -0
- multi_agent_brief/audit/harness.py +242 -0
- multi_agent_brief/audit/interfaces.py +96 -0
- multi_agent_brief/audit/limitation_hygiene.py +238 -0
- multi_agent_brief/audit/proposal_boundary.py +34 -0
- multi_agent_brief/audit/redaction.py +27 -0
- multi_agent_brief/audit/rule_packs.py +96 -0
- multi_agent_brief/audit/semantic.py +435 -0
- multi_agent_brief/capabilities/__init__.py +22 -0
- multi_agent_brief/capabilities/catalog.py +215 -0
- multi_agent_brief/capabilities/detect.py +199 -0
- multi_agent_brief/capabilities/models.py +60 -0
- multi_agent_brief/capabilities/recommend.py +206 -0
- multi_agent_brief/cli/__init__.py +2 -0
- multi_agent_brief/cli/analysis_commands.py +140 -0
- multi_agent_brief/cli/approval_commands.py +107 -0
- multi_agent_brief/cli/audit_commands.py +59 -0
- multi_agent_brief/cli/capability_commands.py +548 -0
- multi_agent_brief/cli/claude_commands.py +159 -0
- multi_agent_brief/cli/competitors_commands.py +182 -0
- multi_agent_brief/cli/controls_commands.py +137 -0
- multi_agent_brief/cli/deliver_commands.py +942 -0
- multi_agent_brief/cli/eval_cases_commands.py +99 -0
- multi_agent_brief/cli/experiments_commands.py +395 -0
- multi_agent_brief/cli/feedback_commands.py +192 -0
- multi_agent_brief/cli/finalize_commands.py +153 -0
- multi_agent_brief/cli/gates_commands.py +174 -0
- multi_agent_brief/cli/hermes_commands.py +356 -0
- multi_agent_brief/cli/improve_commands.py +224 -0
- multi_agent_brief/cli/init_commands.py +564 -0
- multi_agent_brief/cli/init_wizard.py +1473 -0
- multi_agent_brief/cli/input_commands.py +237 -0
- multi_agent_brief/cli/main.py +288 -0
- multi_agent_brief/cli/onboard_commands.py +193 -0
- multi_agent_brief/cli/product_commands.py +1449 -0
- multi_agent_brief/cli/provenance_commands.py +100 -0
- multi_agent_brief/cli/release_commands.py +68 -0
- multi_agent_brief/cli/repair_commands.py +169 -0
- multi_agent_brief/cli/run_commands.py +292 -0
- multi_agent_brief/cli/runtime_commands.py +81 -0
- multi_agent_brief/cli/secrets_commands.py +232 -0
- multi_agent_brief/cli/semantic_support_commands.py +80 -0
- multi_agent_brief/cli/sources_commands.py +647 -0
- multi_agent_brief/cli/start_commands.py +8 -0
- multi_agent_brief/cli/state_commands.py +446 -0
- multi_agent_brief/cli/status_commands.py +26 -0
- multi_agent_brief/cli/workbuddy_commands.py +103 -0
- multi_agent_brief/configs/artifact_contracts.yaml +514 -0
- multi_agent_brief/configs/orchestrator_contract.yaml +181 -0
- multi_agent_brief/configs/policy_packs/default.yaml +65 -0
- multi_agent_brief/configs/policy_profiles/evidence_extract_default.yaml +58 -0
- multi_agent_brief/configs/policy_profiles/finance_default.yaml +44 -0
- multi_agent_brief/configs/policy_profiles/internet_default.yaml +44 -0
- multi_agent_brief/configs/policy_profiles/manufacturing_default.yaml +33 -0
- multi_agent_brief/configs/policy_profiles/solar_manufacturing_default.yaml +64 -0
- multi_agent_brief/configs/report_packs/evidence_extract.yaml +48 -0
- multi_agent_brief/configs/report_packs/management_monthly.yaml +36 -0
- multi_agent_brief/configs/report_packs/market_weekly.yaml +36 -0
- multi_agent_brief/configs/report_packs/solar_industry_periodic.yaml +50 -0
- multi_agent_brief/configs/report_templates/evidence_extract.yaml +55 -0
- multi_agent_brief/configs/report_templates/management_monthly.yaml +48 -0
- multi_agent_brief/configs/report_templates/market_weekly.yaml +49 -0
- multi_agent_brief/configs/report_templates/solar_industry_periodic.yaml +75 -0
- multi_agent_brief/configs/stage_specs.yaml +180 -0
- multi_agent_brief/contracts/__init__.py +41 -0
- multi_agent_brief/contracts/base.py +99 -0
- multi_agent_brief/contracts/errors.py +41 -0
- multi_agent_brief/contracts/migrations/__init__.py +5 -0
- multi_agent_brief/contracts/migrations/claim_v1_to_v2.py +33 -0
- multi_agent_brief/contracts/registry.py +159 -0
- multi_agent_brief/contracts/role_topology.py +25 -0
- multi_agent_brief/contracts/schemas/__init__.py +35 -0
- multi_agent_brief/contracts/schemas/analysis_pack.py +135 -0
- multi_agent_brief/contracts/schemas/atomic_claim_graph.py +304 -0
- multi_agent_brief/contracts/schemas/audit_report.py +80 -0
- multi_agent_brief/contracts/schemas/candidate_item.py +51 -0
- multi_agent_brief/contracts/schemas/claim.py +197 -0
- multi_agent_brief/contracts/schemas/claim_draft.py +386 -0
- multi_agent_brief/contracts/schemas/claim_support_matrix.py +334 -0
- multi_agent_brief/contracts/schemas/evidence_span_registry.py +270 -0
- multi_agent_brief/contracts/schemas/policy_profile.py +247 -0
- multi_agent_brief/contracts/schemas/report_spec.py +257 -0
- multi_agent_brief/contracts/schemas/semantic_assessment_report.py +480 -0
- multi_agent_brief/contracts/schemas/source_evidence_pack_manifest.py +233 -0
- multi_agent_brief/contracts/schemas/source_item.py +96 -0
- multi_agent_brief/contracts/semantic_assessment_status.py +12 -0
- multi_agent_brief/contracts/source_metadata.py +252 -0
- multi_agent_brief/contracts/target_contract.py +460 -0
- multi_agent_brief/contracts/validator.py +225 -0
- multi_agent_brief/controls/__init__.py +23 -0
- multi_agent_brief/controls/contract.py +106 -0
- multi_agent_brief/controls/switchboard.py +1076 -0
- multi_agent_brief/core/__init__.py +2 -0
- multi_agent_brief/core/citations.py +14 -0
- multi_agent_brief/core/claim_ledger.py +94 -0
- multi_agent_brief/core/config.py +191 -0
- multi_agent_brief/core/env.py +69 -0
- multi_agent_brief/core/previous.py +83 -0
- multi_agent_brief/core/schemas.py +181 -0
- multi_agent_brief/core/selection.py +189 -0
- multi_agent_brief/delivery/__init__.py +6 -0
- multi_agent_brief/delivery/base.py +36 -0
- multi_agent_brief/delivery/feishu.py +204 -0
- multi_agent_brief/delivery/gws.py +237 -0
- multi_agent_brief/evaluation_cases/__init__.py +15 -0
- multi_agent_brief/evaluation_cases/contract.py +429 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/improvement/ledger.jsonl +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/config.yaml +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/sources.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/config.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/input/human_feedback.md +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/brief.md +14 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audited_brief.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/config.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/user.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/config.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/feedback_issues.json +25 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/repair_plan.json +27 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/claim_ledger.json +11 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/quality_gate_report.json +22 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audited_brief.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/claim_ledger.json +40 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/sources.yaml +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/claim_ledger.json +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/output/intermediate/release_readiness_report.json +31 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/ledger.jsonl +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/memory.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/output/intermediate/improvement_memory_snapshot.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/config.yaml +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/input/sources/source-001.txt +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/delivery/brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/atomic_claim_graph.json +17 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audit_report.json +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audited_brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/claim_ledger.json +20 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/evidence_span_registry.json +20 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/finalize_report.json +40 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/auditor_quality_gate_report.json +42 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/finalize_quality_gate_report.json +42 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/screened_candidates.json +33 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix.md +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix_trace.md +10 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/report_spec.yaml +24 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/sources.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/input/sources/README.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/output/intermediate/source_evidence_pack_manifest.json +26 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/config.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/claim_ledger.json +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/config.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/improvement/ledger.jsonl +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/user.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audited_brief.md +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/manifest.yaml +861 -0
- multi_agent_brief/evaluation_cases/fixtures.py +20 -0
- multi_agent_brief/evaluation_cases/runner.py +1603 -0
- multi_agent_brief/experiments/__init__.py +45 -0
- multi_agent_brief/experiments/experiment_080.py +5835 -0
- multi_agent_brief/experiments/schemas.py +63 -0
- multi_agent_brief/experiments/target_contract.py +10 -0
- multi_agent_brief/feedback/__init__.py +17 -0
- multi_agent_brief/feedback/feedback_contract.py +482 -0
- multi_agent_brief/feedback/feedback_state.py +898 -0
- multi_agent_brief/hermes/__init__.py +25 -0
- multi_agent_brief/hermes/adapter.py +832 -0
- multi_agent_brief/improvement/__init__.py +83 -0
- multi_agent_brief/improvement/contract.py +1114 -0
- multi_agent_brief/improvement/memory.py +451 -0
- multi_agent_brief/improvement/product_definition.py +228 -0
- multi_agent_brief/improvement/state.py +961 -0
- multi_agent_brief/inputs/__init__.py +2 -0
- multi_agent_brief/inputs/classifier.py +322 -0
- multi_agent_brief/inputs/contracts.py +59 -0
- multi_agent_brief/inputs/extractor.py +310 -0
- multi_agent_brief/install/__init__.py +2 -0
- multi_agent_brief/install/writer.py +97 -0
- multi_agent_brief/onboarding/__init__.py +11 -0
- multi_agent_brief/onboarding/io.py +103 -0
- multi_agent_brief/onboarding/mapper.py +484 -0
- multi_agent_brief/onboarding/schema.py +46 -0
- multi_agent_brief/orchestrator/__init__.py +2 -0
- multi_agent_brief/orchestrator/fact_layer_import.py +352 -0
- multi_agent_brief/orchestrator/handoff.py +2209 -0
- multi_agent_brief/orchestrator/role_topology.py +42 -0
- multi_agent_brief/orchestrator/run_archive.py +888 -0
- multi_agent_brief/orchestrator/run_integrity.py +346 -0
- multi_agent_brief/orchestrator/runtime_state/__init__.py +70 -0
- multi_agent_brief/orchestrator/runtime_state/_io.py +167 -0
- multi_agent_brief/orchestrator/runtime_state/_transactions.py +298 -0
- multi_agent_brief/orchestrator/runtime_state/artifact_registry.py +1825 -0
- multi_agent_brief/orchestrator/runtime_state/atomic_claim_graph.py +96 -0
- multi_agent_brief/orchestrator/runtime_state/claim_ledger_freeze.py +510 -0
- multi_agent_brief/orchestrator/runtime_state/claim_metadata_enrichment.py +836 -0
- multi_agent_brief/orchestrator/runtime_state/claim_support_matrix.py +582 -0
- multi_agent_brief/orchestrator/runtime_state/completion_gates.py +672 -0
- multi_agent_brief/orchestrator/runtime_state/contracts_loader.py +66 -0
- multi_agent_brief/orchestrator/runtime_state/decisions.py +239 -0
- multi_agent_brief/orchestrator/runtime_state/errors.py +61 -0
- multi_agent_brief/orchestrator/runtime_state/event_log.py +256 -0
- multi_agent_brief/orchestrator/runtime_state/evidence_span_registry.py +233 -0
- multi_agent_brief/orchestrator/runtime_state/fact_layer.py +755 -0
- multi_agent_brief/orchestrator/runtime_state/identity.py +90 -0
- multi_agent_brief/orchestrator/runtime_state/lifecycle.py +675 -0
- multi_agent_brief/orchestrator/runtime_state/manifest.py +82 -0
- multi_agent_brief/orchestrator/runtime_state/operations.py +224 -0
- multi_agent_brief/orchestrator/runtime_state/paths.py +38 -0
- multi_agent_brief/orchestrator/runtime_state/repair.py +984 -0
- multi_agent_brief/orchestrator/runtime_state/safety_surfaces.py +147 -0
- multi_agent_brief/orchestrator/runtime_state/semantic_assessment_report.py +692 -0
- multi_agent_brief/orchestrator/runtime_state/semantic_support_acceptance.py +683 -0
- multi_agent_brief/orchestrator/runtime_state/source_evidence_pack.py +131 -0
- multi_agent_brief/orchestrator/runtime_state/stage_completion.py +1320 -0
- multi_agent_brief/orchestrator/runtime_state/trajectory.py +200 -0
- multi_agent_brief/orchestrator/runtime_state/workflow.py +350 -0
- multi_agent_brief/orchestrator/source_evidence.py +43 -0
- multi_agent_brief/orchestrator/timing.py +410 -0
- multi_agent_brief/orchestrator_contract.py +96 -0
- multi_agent_brief/outputs/__init__.py +2 -0
- multi_agent_brief/outputs/atomic_reader_projection.py +288 -0
- multi_agent_brief/outputs/docx.py +32 -0
- multi_agent_brief/outputs/finalize.py +1036 -0
- multi_agent_brief/outputs/ib_docx.py +983 -0
- multi_agent_brief/outputs/naming.py +38 -0
- multi_agent_brief/outputs/pdf.py +20 -0
- multi_agent_brief/outputs/reader_final_gate.py +509 -0
- multi_agent_brief/outputs/reader_projection.py +397 -0
- multi_agent_brief/outputs/source_appendix.py +753 -0
- multi_agent_brief/outputs/templates/__init__.py +112 -0
- multi_agent_brief/product/__init__.py +49 -0
- multi_agent_brief/product/bundle_projection.py +686 -0
- multi_agent_brief/product/citation_profile.py +145 -0
- multi_agent_brief/product/guidance_manifestation.py +421 -0
- multi_agent_brief/product/materiality_selection.py +472 -0
- multi_agent_brief/product/policy_gate_adapter.py +116 -0
- multi_agent_brief/product/policy_profile.py +39 -0
- multi_agent_brief/product/policy_projection.py +161 -0
- multi_agent_brief/product/policy_registry.py +83 -0
- multi_agent_brief/product/policy_resolver.py +225 -0
- multi_agent_brief/product/quality_closeout.py +188 -0
- multi_agent_brief/product/quality_panel.py +2217 -0
- multi_agent_brief/product/release_approval.py +1034 -0
- multi_agent_brief/product/report_pack.py +130 -0
- multi_agent_brief/product/report_pack_aliases.py +68 -0
- multi_agent_brief/product/report_registry.py +101 -0
- multi_agent_brief/product/report_spec.py +181 -0
- multi_agent_brief/product/support_wording.py +525 -0
- multi_agent_brief/product/template_conformance.py +516 -0
- multi_agent_brief/product/template_projection.py +123 -0
- multi_agent_brief/product/template_registry.py +244 -0
- multi_agent_brief/product/template_render_plan.py +315 -0
- multi_agent_brief/product/template_renderer.py +224 -0
- multi_agent_brief/product/trajectory_regulation.py +441 -0
- multi_agent_brief/provenance/__init__.py +5 -0
- multi_agent_brief/provenance/builder.py +841 -0
- multi_agent_brief/provenance/contract.py +21 -0
- multi_agent_brief/provenance/io.py +144 -0
- multi_agent_brief/provenance/model.py +110 -0
- multi_agent_brief/provenance/references.py +43 -0
- multi_agent_brief/provenance/validator.py +135 -0
- multi_agent_brief/quality_gates/__init__.py +19 -0
- multi_agent_brief/quality_gates/contract.py +581 -0
- multi_agent_brief/quality_gates/state.py +2498 -0
- multi_agent_brief/repair/__init__.py +5 -0
- multi_agent_brief/repair/router.py +842 -0
- multi_agent_brief/runtime_assets.py +431 -0
- multi_agent_brief/sources/__init__.py +16 -0
- multi_agent_brief/sources/api_filings.py +236 -0
- multi_agent_brief/sources/api_news.py +174 -0
- multi_agent_brief/sources/base.py +142 -0
- multi_agent_brief/sources/cached_package.py +119 -0
- multi_agent_brief/sources/cli_provider.py +263 -0
- multi_agent_brief/sources/coverage.py +336 -0
- multi_agent_brief/sources/decider.py +756 -0
- multi_agent_brief/sources/doctor.py +373 -0
- multi_agent_brief/sources/evidence_pack.py +364 -0
- multi_agent_brief/sources/feishu_provider.py +326 -0
- multi_agent_brief/sources/filing_resolver.py +400 -0
- multi_agent_brief/sources/industry_packs.py +130 -0
- multi_agent_brief/sources/join.py +206 -0
- multi_agent_brief/sources/local_signal.py +81 -0
- multi_agent_brief/sources/local_signal_planner.py +636 -0
- multi_agent_brief/sources/manual.py +259 -0
- multi_agent_brief/sources/mcp_provider.py +309 -0
- multi_agent_brief/sources/mineru_provider.py +601 -0
- multi_agent_brief/sources/normalizer.py +95 -0
- multi_agent_brief/sources/opencli_provider.py +282 -0
- multi_agent_brief/sources/planner.py +178 -0
- multi_agent_brief/sources/registry.py +367 -0
- multi_agent_brief/sources/rss.py +190 -0
- multi_agent_brief/sources/search_backends/__init__.py +31 -0
- multi_agent_brief/sources/search_backends/base.py +51 -0
- multi_agent_brief/sources/search_backends/brave.py +192 -0
- multi_agent_brief/sources/search_backends/capabilities.py +94 -0
- multi_agent_brief/sources/search_backends/exa.py +180 -0
- multi_agent_brief/sources/search_backends/firecrawl.py +164 -0
- multi_agent_brief/sources/search_backends/serper.py +241 -0
- multi_agent_brief/sources/search_backends/tavily.py +124 -0
- multi_agent_brief/sources/sourcehub.py +505 -0
- multi_agent_brief/sources/web_search.py +267 -0
- multi_agent_brief/status.py +966 -0
- multi_agent_brief/tools/__init__.py +3 -0
- multi_agent_brief/tools/draft_cleanup.py +198 -0
- multi_agent_brief/workbuddy/__init__.py +1 -0
- multi_agent_brief/workbuddy/diagnose.py +569 -0
- multi_agent_brief/workbuddy/skill_pack.py +309 -0
- multi_agent_brief/workspace/__init__.py +1 -0
- multi_agent_brief/workspace/init_profile.py +45 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Shared helpers for internal claim citation markers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
CLAIM_ID_RE_FRAGMENT = r"[A-Za-z0-9][A-Za-z0-9_-]{1,127}"
|
|
9
|
+
SRC_REF_PATTERN = re.compile(rf"\[src:({CLAIM_ID_RE_FRAGMENT})\]")
|
|
10
|
+
VALID_SRC_REF_PATTERN = re.compile(rf"\[src:{CLAIM_ID_RE_FRAGMENT}\]")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def extract_src_ref_ids(markdown: str) -> list[str]:
|
|
14
|
+
return SRC_REF_PATTERN.findall(markdown)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from multi_agent_brief.core.schemas import Claim
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ClaimLedger:
|
|
12
|
+
"""In-memory claim store with validation helpers."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, claims: list[Claim] | None = None) -> None:
|
|
15
|
+
self._claims: dict[str, Claim] = {}
|
|
16
|
+
for claim in claims or []:
|
|
17
|
+
self.add_claim(claim)
|
|
18
|
+
|
|
19
|
+
def __len__(self) -> int:
|
|
20
|
+
return len(self._claims)
|
|
21
|
+
|
|
22
|
+
def __iter__(self):
|
|
23
|
+
return iter(self._claims.values())
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def build_claim_id(statement: str, source_id: str) -> str:
|
|
27
|
+
digest = hashlib.sha1(f"{source_id}|{statement}".encode("utf-8")).hexdigest()[:10]
|
|
28
|
+
prefix = "".join(ch for ch in source_id.upper() if ch.isalnum())[:8] or "CLAIM"
|
|
29
|
+
return f"{prefix}_{digest.upper()}"
|
|
30
|
+
|
|
31
|
+
def add_claim(self, claim: Claim) -> Claim:
|
|
32
|
+
if not claim.claim_id:
|
|
33
|
+
claim.claim_id = self.build_claim_id(claim.statement, claim.source_id)
|
|
34
|
+
self._claims[claim.claim_id] = claim
|
|
35
|
+
return claim
|
|
36
|
+
|
|
37
|
+
def get_claim(self, claim_id: str) -> Claim | None:
|
|
38
|
+
return self._claims.get(claim_id)
|
|
39
|
+
|
|
40
|
+
def to_list(self) -> list[dict]:
|
|
41
|
+
return [claim.to_dict() for claim in self._claims.values()]
|
|
42
|
+
|
|
43
|
+
def export_json(self, path: str | Path) -> None:
|
|
44
|
+
Path(path).write_text(json.dumps(self.to_list(), ensure_ascii=False, indent=2), encoding="utf-8")
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def import_json(cls, path: str | Path) -> "ClaimLedger":
|
|
48
|
+
data = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
49
|
+
claims_data = cls._claim_items_from_json(data)
|
|
50
|
+
return cls([Claim.from_dict(item) for item in claims_data])
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def _claim_items_from_json(data: Any) -> list[dict[str, Any]]:
|
|
54
|
+
if isinstance(data, list):
|
|
55
|
+
claims = data
|
|
56
|
+
elif isinstance(data, dict):
|
|
57
|
+
claims = None
|
|
58
|
+
for key in ("claims", "claim_ledger", "items"):
|
|
59
|
+
value = data.get(key)
|
|
60
|
+
if isinstance(value, list):
|
|
61
|
+
claims = value
|
|
62
|
+
break
|
|
63
|
+
if claims is None:
|
|
64
|
+
raise ValueError("Claim Ledger JSON object must contain a claims list.")
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError("Claim Ledger JSON must be a list or an object containing a claims list.")
|
|
67
|
+
if not all(isinstance(item, dict) for item in claims):
|
|
68
|
+
raise ValueError("Claim Ledger claims must be JSON objects.")
|
|
69
|
+
return claims
|
|
70
|
+
|
|
71
|
+
def detect_missing_sources(self) -> list[Claim]:
|
|
72
|
+
return [
|
|
73
|
+
claim
|
|
74
|
+
for claim in self._claims.values()
|
|
75
|
+
if not claim.source_id or not claim.evidence_text.strip()
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
def detect_duplicate_claims(self) -> list[list[Claim]]:
|
|
79
|
+
buckets: dict[str, list[Claim]] = {}
|
|
80
|
+
for claim in self._claims.values():
|
|
81
|
+
normalized = " ".join(claim.statement.lower().split())
|
|
82
|
+
buckets.setdefault(normalized, []).append(claim)
|
|
83
|
+
return [claims for claims in buckets.values() if len(claims) > 1]
|
|
84
|
+
|
|
85
|
+
def validate_claims(self) -> list[str]:
|
|
86
|
+
errors: list[str] = []
|
|
87
|
+
for claim in self._claims.values():
|
|
88
|
+
if not claim.statement.strip():
|
|
89
|
+
errors.append(f"{claim.claim_id}: missing statement")
|
|
90
|
+
if claim.requires_audit and not claim.evidence_text.strip():
|
|
91
|
+
errors.append(f"{claim.claim_id}: missing evidence_text")
|
|
92
|
+
if not claim.source_id.strip():
|
|
93
|
+
errors.append(f"{claim.claim_id}: missing source_id")
|
|
94
|
+
return errors
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from multi_agent_brief.outputs.naming import DEFAULT_FILENAME_TEMPLATE
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import yaml
|
|
10
|
+
except ModuleNotFoundError: # pragma: no cover - exercised when PyYAML is not installed
|
|
11
|
+
yaml = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _parse_scalar(value: str) -> Any:
|
|
15
|
+
value = value.strip()
|
|
16
|
+
if value in {"true", "false"}:
|
|
17
|
+
return value == "true"
|
|
18
|
+
if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")):
|
|
19
|
+
return value[1:-1]
|
|
20
|
+
return value
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _minimal_yaml_load(text: str) -> dict[str, Any]:
|
|
24
|
+
"""Small fallback parser for the simple config files used by the MVP."""
|
|
25
|
+
data: dict[str, Any] = {}
|
|
26
|
+
current_section: dict[str, Any] | None = None
|
|
27
|
+
|
|
28
|
+
for raw_line in text.splitlines():
|
|
29
|
+
if not raw_line.strip() or raw_line.lstrip().startswith("#"):
|
|
30
|
+
continue
|
|
31
|
+
indent = len(raw_line) - len(raw_line.lstrip(" "))
|
|
32
|
+
line = raw_line.strip()
|
|
33
|
+
if indent == 0 and line.endswith(":"):
|
|
34
|
+
section_name = line[:-1]
|
|
35
|
+
current_section = {}
|
|
36
|
+
data[section_name] = current_section
|
|
37
|
+
continue
|
|
38
|
+
if ":" not in line:
|
|
39
|
+
continue
|
|
40
|
+
key, value = line.split(":", 1)
|
|
41
|
+
target = current_section if indent > 0 and current_section is not None else data
|
|
42
|
+
target[key.strip()] = _parse_scalar(value)
|
|
43
|
+
return data
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def load_config(path: str | Path) -> dict[str, Any]:
|
|
47
|
+
config_path = Path(path)
|
|
48
|
+
if not config_path.exists():
|
|
49
|
+
raise FileNotFoundError(f"Config file not found: {config_path}")
|
|
50
|
+
config_text = config_path.read_text(encoding="utf-8")
|
|
51
|
+
data = yaml.safe_load(config_text) if yaml is not None else _minimal_yaml_load(config_text)
|
|
52
|
+
data = data or {}
|
|
53
|
+
if not isinstance(data, dict):
|
|
54
|
+
raise ValueError(f"Config must be a mapping: {config_path}")
|
|
55
|
+
data = normalize_language_config(data)
|
|
56
|
+
data["_config_dir"] = str(config_path.parent)
|
|
57
|
+
return data
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def normalize_language_config(config: dict[str, Any]) -> dict[str, Any]:
|
|
61
|
+
project = config.get("project", {}) or {}
|
|
62
|
+
language_config = config.get("language", {}) or {}
|
|
63
|
+
if not isinstance(language_config, dict):
|
|
64
|
+
language_config = {}
|
|
65
|
+
|
|
66
|
+
output_language = language_config.get("output") or project.get("language") or "zh-CN"
|
|
67
|
+
interface_language = language_config.get("interface") or output_language
|
|
68
|
+
source_handling = language_config.get("source_handling") or "preserve_original"
|
|
69
|
+
|
|
70
|
+
config["language"] = {
|
|
71
|
+
**language_config,
|
|
72
|
+
"interface": interface_language,
|
|
73
|
+
"output": output_language,
|
|
74
|
+
"source_handling": source_handling,
|
|
75
|
+
}
|
|
76
|
+
return config
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_output_config(config: dict[str, Any] | None) -> dict[str, Any]:
|
|
80
|
+
"""Return canonical output config, with legacy outputs alias support."""
|
|
81
|
+
config = config or {}
|
|
82
|
+
output_config = config.get("output")
|
|
83
|
+
if isinstance(output_config, dict):
|
|
84
|
+
return output_config
|
|
85
|
+
legacy_output_config = config.get("outputs")
|
|
86
|
+
if isinstance(legacy_output_config, dict):
|
|
87
|
+
return legacy_output_config
|
|
88
|
+
return {}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _as_bool(value: Any, default: bool = False) -> bool:
|
|
92
|
+
if value is None:
|
|
93
|
+
return default
|
|
94
|
+
if isinstance(value, bool):
|
|
95
|
+
return value
|
|
96
|
+
if isinstance(value, str):
|
|
97
|
+
return value.strip().lower() in {"1", "true", "yes", "y", "on"}
|
|
98
|
+
return bool(value)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def build_run_settings(
|
|
102
|
+
*,
|
|
103
|
+
config: dict[str, Any] | None,
|
|
104
|
+
input_dir: str | None,
|
|
105
|
+
output_dir: str | None,
|
|
106
|
+
name: str | None,
|
|
107
|
+
language: str | None,
|
|
108
|
+
audience: str | None,
|
|
109
|
+
) -> dict[str, Any]:
|
|
110
|
+
config = config or {}
|
|
111
|
+
project = config.get("project", {}) or {}
|
|
112
|
+
language_config = config.get("language", {}) or {}
|
|
113
|
+
report = config.get("report", {}) or {}
|
|
114
|
+
previous = config.get("previous", {}) or {}
|
|
115
|
+
selection = config.get("selection", {}) or {}
|
|
116
|
+
|
|
117
|
+
# Resolve report.date: "auto" → today's date
|
|
118
|
+
raw_date = report.get("date", "")
|
|
119
|
+
if raw_date == "auto":
|
|
120
|
+
from datetime import date
|
|
121
|
+
raw_date = date.today().isoformat()
|
|
122
|
+
selector = config.get("selector", {}) or {}
|
|
123
|
+
input_config = config.get("input", {}) or {}
|
|
124
|
+
output_config = get_output_config(config)
|
|
125
|
+
config_dir = Path(config.get("_config_dir", "."))
|
|
126
|
+
|
|
127
|
+
resolved_input = input_dir or input_config.get("path")
|
|
128
|
+
if not resolved_input:
|
|
129
|
+
raise ValueError("Input directory is required. Pass input_dir or set input.path in config.")
|
|
130
|
+
|
|
131
|
+
input_path = Path(str(resolved_input))
|
|
132
|
+
if input_dir is None and not input_path.is_absolute():
|
|
133
|
+
input_path = config_dir / input_path
|
|
134
|
+
|
|
135
|
+
resolved_output = output_dir or output_config.get("path") or "output/demo"
|
|
136
|
+
output_path = Path(str(resolved_output))
|
|
137
|
+
if output_dir is None and output_config.get("path") and not output_path.is_absolute():
|
|
138
|
+
output_path = config_dir / output_path
|
|
139
|
+
|
|
140
|
+
project_name = name or project.get("name") or "Weekly Intelligence Brief"
|
|
141
|
+
resolved_language = language or language_config.get("output") or project.get("language") or "zh-CN"
|
|
142
|
+
resolved_audience = audience or project.get("audience") or "management"
|
|
143
|
+
|
|
144
|
+
# Resolve audience profile from config or map from audience text
|
|
145
|
+
audience_profile_config = config.get("audience_profile", {}) or {}
|
|
146
|
+
resolved_audience_profile = audience_profile_config.get("id", "")
|
|
147
|
+
if not resolved_audience_profile:
|
|
148
|
+
from multi_agent_brief.audience.profiles import map_audience_to_profile
|
|
149
|
+
resolved_audience_profile = map_audience_to_profile(resolved_audience)
|
|
150
|
+
|
|
151
|
+
output_filename_template = (
|
|
152
|
+
output_config.get("filename_template")
|
|
153
|
+
or output_config.get("file_name_template")
|
|
154
|
+
or output_config.get("name_template")
|
|
155
|
+
or output_config.get("filename")
|
|
156
|
+
or DEFAULT_FILENAME_TEMPLATE
|
|
157
|
+
)
|
|
158
|
+
output_filename_tokens = {
|
|
159
|
+
"project_name": str(project_name),
|
|
160
|
+
"title": str(project_name),
|
|
161
|
+
"company": str(project.get("company", "")),
|
|
162
|
+
"industry": str(project.get("industry", "")),
|
|
163
|
+
"audience": str(resolved_audience),
|
|
164
|
+
"cadence": str(report.get("cadence", "")),
|
|
165
|
+
"report_date": str(raw_date),
|
|
166
|
+
"date": str(raw_date),
|
|
167
|
+
"language": str(resolved_language),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
"project_name": project_name,
|
|
172
|
+
"input_dir": str(input_path),
|
|
173
|
+
"output_dir": str(output_path),
|
|
174
|
+
"language": resolved_language,
|
|
175
|
+
"audience": resolved_audience,
|
|
176
|
+
"audience_profile": resolved_audience_profile,
|
|
177
|
+
"report_date": str(raw_date),
|
|
178
|
+
"max_source_age_days": int(report["max_source_age_days"]) if "max_source_age_days" in report else None,
|
|
179
|
+
"fail_on_stale_source": _as_bool(report.get("fail_on_stale_source"), False),
|
|
180
|
+
"previous_report_dir": str(previous.get("path", "")),
|
|
181
|
+
"max_claims": (
|
|
182
|
+
int(selector["max_items"]) if selector.get("max_items") is not None
|
|
183
|
+
else int(selection.get("max_claims", 160))
|
|
184
|
+
),
|
|
185
|
+
"quiet_week_min_claims": int(selection.get("quiet_week_min_claims", 5)),
|
|
186
|
+
"output_formats": output_config.get("formats", ["markdown"]),
|
|
187
|
+
"output_footer": output_config.get("footer", ""),
|
|
188
|
+
"output_filename_template": str(output_filename_template),
|
|
189
|
+
"output_filename_tokens": output_filename_tokens,
|
|
190
|
+
"output_named_outputs": _as_bool(output_config.get("named_outputs"), True),
|
|
191
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Safe workspace environment helpers."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
KNOWN_WORKSPACE_ENV_KEYS = frozenset(
|
|
9
|
+
{
|
|
10
|
+
"TAVILY_API_KEY",
|
|
11
|
+
"EXA_API_KEY",
|
|
12
|
+
"BRAVE_SEARCH_API_KEY",
|
|
13
|
+
"FIRECRAWL_API_KEY",
|
|
14
|
+
"SERPER_API_KEY",
|
|
15
|
+
"NEWSAPI_API_KEY",
|
|
16
|
+
"MINERU_API_TOKEN",
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _parse_env_line(line: str) -> tuple[str, str] | None:
|
|
22
|
+
text = line.strip()
|
|
23
|
+
if not text or text.startswith("#") or "=" not in text:
|
|
24
|
+
return None
|
|
25
|
+
if text.startswith("export "):
|
|
26
|
+
text = text[len("export "):].lstrip()
|
|
27
|
+
key, value = text.split("=", 1)
|
|
28
|
+
key = key.strip()
|
|
29
|
+
if key not in KNOWN_WORKSPACE_ENV_KEYS:
|
|
30
|
+
return None
|
|
31
|
+
value = value.strip()
|
|
32
|
+
if (
|
|
33
|
+
len(value) >= 2
|
|
34
|
+
and value[0] == value[-1]
|
|
35
|
+
and value[0] in {"'", '"'}
|
|
36
|
+
):
|
|
37
|
+
value = value[1:-1]
|
|
38
|
+
return key, value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def read_workspace_env_key(workspace_dir: str | Path | None, key: str) -> str:
|
|
42
|
+
"""Return a known env key from workspace .env without exporting it."""
|
|
43
|
+
if key not in KNOWN_WORKSPACE_ENV_KEYS or not workspace_dir:
|
|
44
|
+
return ""
|
|
45
|
+
path = Path(workspace_dir) / ".env"
|
|
46
|
+
try:
|
|
47
|
+
lines = path.read_text(encoding="utf-8").splitlines()
|
|
48
|
+
except OSError:
|
|
49
|
+
return ""
|
|
50
|
+
for line in lines:
|
|
51
|
+
parsed = _parse_env_line(line)
|
|
52
|
+
if not parsed:
|
|
53
|
+
continue
|
|
54
|
+
parsed_key, value = parsed
|
|
55
|
+
if parsed_key == key and value:
|
|
56
|
+
return value
|
|
57
|
+
return ""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_known_env_value(key: str, workspace_dir: str | Path | None = None) -> str:
|
|
61
|
+
"""Return shell env value first, then known workspace .env value."""
|
|
62
|
+
if key not in KNOWN_WORKSPACE_ENV_KEYS:
|
|
63
|
+
return ""
|
|
64
|
+
return os.environ.get(key, "") or read_workspace_env_key(workspace_dir, key)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def known_env_key_is_set(key: str, workspace_dir: str | Path | None = None) -> bool:
|
|
68
|
+
"""Return whether a known key is set without exposing its value."""
|
|
69
|
+
return bool(get_known_env_value(key, workspace_dir))
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import zipfile
|
|
5
|
+
import xml.etree.ElementTree as ET
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_previous_report_baseline(directory: str | Path, report_date: str = "", limit: int = 5) -> tuple[str, list[str]]:
|
|
11
|
+
"""Load previous md/txt/docx reports as a deduplication baseline."""
|
|
12
|
+
root = Path(directory)
|
|
13
|
+
if not directory or not root.exists():
|
|
14
|
+
return "", []
|
|
15
|
+
|
|
16
|
+
current = _parse_report_date(report_date)
|
|
17
|
+
candidates = [
|
|
18
|
+
path
|
|
19
|
+
for path in root.iterdir()
|
|
20
|
+
if path.is_file() and path.suffix.lower() in {".md", ".txt", ".docx"}
|
|
21
|
+
]
|
|
22
|
+
eligible = []
|
|
23
|
+
for path in candidates:
|
|
24
|
+
file_date = _date_from_name(path)
|
|
25
|
+
if current and file_date and file_date >= current:
|
|
26
|
+
continue
|
|
27
|
+
eligible.append(path)
|
|
28
|
+
|
|
29
|
+
eligible.sort(key=lambda path: (_date_from_name(path) or datetime.min.date(), path.stat().st_mtime), reverse=True)
|
|
30
|
+
|
|
31
|
+
parts: list[str] = []
|
|
32
|
+
names: list[str] = []
|
|
33
|
+
for path in eligible[:limit]:
|
|
34
|
+
text = _read_report_text(path).strip()
|
|
35
|
+
if not text:
|
|
36
|
+
continue
|
|
37
|
+
names.append(path.name)
|
|
38
|
+
parts.append(f"===== {path.name} =====\n{text}")
|
|
39
|
+
return "\n\n".join(parts), names
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _parse_report_date(value: str):
|
|
43
|
+
if not value:
|
|
44
|
+
return None
|
|
45
|
+
for fmt in ("%Y%m%d", "%Y-%m-%d"):
|
|
46
|
+
try:
|
|
47
|
+
return datetime.strptime(value, fmt).date()
|
|
48
|
+
except ValueError:
|
|
49
|
+
continue
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _date_from_name(path: Path):
|
|
54
|
+
match = re.search(r"(20\d{6})", path.name)
|
|
55
|
+
if not match:
|
|
56
|
+
return None
|
|
57
|
+
return _parse_report_date(match.group(1))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _read_report_text(path: Path) -> str:
|
|
61
|
+
if path.suffix.lower() == ".docx":
|
|
62
|
+
return _extract_docx_text(path)
|
|
63
|
+
try:
|
|
64
|
+
return path.read_text(encoding="utf-8")
|
|
65
|
+
except UnicodeDecodeError:
|
|
66
|
+
return path.read_text(errors="ignore")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _extract_docx_text(path: Path) -> str:
|
|
70
|
+
try:
|
|
71
|
+
with zipfile.ZipFile(path) as zf:
|
|
72
|
+
xml = zf.read("word/document.xml")
|
|
73
|
+
except Exception:
|
|
74
|
+
return ""
|
|
75
|
+
root = ET.fromstring(xml)
|
|
76
|
+
ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
|
|
77
|
+
lines = []
|
|
78
|
+
for paragraph in root.findall(".//w:p", ns):
|
|
79
|
+
texts = [node.text for node in paragraph.findall(".//w:t", ns) if node.text]
|
|
80
|
+
if texts:
|
|
81
|
+
lines.append("".join(texts))
|
|
82
|
+
return "\n".join(lines)
|
|
83
|
+
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import asdict, dataclass, field
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
# Re-export SourceItem from the canonical location (sources.base)
|
|
8
|
+
from multi_agent_brief.sources.base import SourceItem # noqa: F401
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
ClaimType = Literal["fact", "number", "date", "interpretation", "forecast", "risk"]
|
|
12
|
+
Confidence = Literal["low", "medium", "high"]
|
|
13
|
+
AuditSeverity = Literal["low", "medium", "high"]
|
|
14
|
+
AuditStatus = Literal["pass", "warning", "fail"]
|
|
15
|
+
|
|
16
|
+
# v2 epistemic types
|
|
17
|
+
SchemaVersion = Literal["v1", "v2"]
|
|
18
|
+
EpistemicType = Literal["observed", "interpreted", "hypothesis", "action", "analogy"]
|
|
19
|
+
EvidenceRelation = Literal["direct", "indirect", "inferred", "analogous"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def utc_now_iso() -> str:
|
|
23
|
+
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class CandidateItem:
|
|
28
|
+
item_id: str
|
|
29
|
+
title: str
|
|
30
|
+
summary: str
|
|
31
|
+
source_id: str
|
|
32
|
+
topic: str = "general"
|
|
33
|
+
importance: str = "medium"
|
|
34
|
+
reason_for_inclusion: str = ""
|
|
35
|
+
|
|
36
|
+
def to_dict(self) -> dict[str, Any]:
|
|
37
|
+
return asdict(self)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class Claim:
|
|
42
|
+
claim_id: str
|
|
43
|
+
statement: str
|
|
44
|
+
source_id: str
|
|
45
|
+
evidence_text: str
|
|
46
|
+
source_url: str = ""
|
|
47
|
+
source_type: str = "local_file"
|
|
48
|
+
claim_type: ClaimType = "fact"
|
|
49
|
+
confidence: Confidence = "medium"
|
|
50
|
+
requires_audit: bool = True
|
|
51
|
+
created_by: str = "scout"
|
|
52
|
+
used_in_sections: list[str] = field(default_factory=list)
|
|
53
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
# v2 epistemic fields — new Claims default to v2
|
|
55
|
+
schema_version: SchemaVersion = "v2"
|
|
56
|
+
epistemic_type: EpistemicType = "observed"
|
|
57
|
+
evidence_relation: EvidenceRelation = "direct"
|
|
58
|
+
applicability_reason: str = ""
|
|
59
|
+
limitations: list[str] = field(default_factory=list)
|
|
60
|
+
|
|
61
|
+
def to_dict(self) -> dict[str, Any]:
|
|
62
|
+
return asdict(self)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_dict(cls, data: dict[str, Any]) -> "Claim":
|
|
66
|
+
known = {f.name for f in cls.__dataclass_fields__.values()}
|
|
67
|
+
filtered = {k: v for k, v in data.items() if k in known}
|
|
68
|
+
claim = cls(**filtered)
|
|
69
|
+
# v1→v2 migration: fill missing epistemic fields from claim_type
|
|
70
|
+
if "epistemic_type" not in data:
|
|
71
|
+
claim.epistemic_type = cls._migrate_epistemic(claim.claim_type)
|
|
72
|
+
if "schema_version" not in data:
|
|
73
|
+
claim.schema_version = "v1"
|
|
74
|
+
return claim
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _migrate_epistemic(claim_type: str) -> EpistemicType:
|
|
78
|
+
mapping: dict[str, EpistemicType] = {
|
|
79
|
+
"interpretation": "interpreted",
|
|
80
|
+
"forecast": "hypothesis",
|
|
81
|
+
"risk": "hypothesis",
|
|
82
|
+
}
|
|
83
|
+
return mapping.get(claim_type, "observed")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class BriefSection:
|
|
88
|
+
title: str
|
|
89
|
+
body: str
|
|
90
|
+
claim_ids: list[str] = field(default_factory=list)
|
|
91
|
+
|
|
92
|
+
def to_dict(self) -> dict[str, Any]:
|
|
93
|
+
return asdict(self)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
BlockingLevel = Literal[
|
|
97
|
+
"editor_fixable", # editor can fix in-place (residue, filler, labels)
|
|
98
|
+
"analyst_blocking", # must return to analyst (unsupported certainty, hypothesis misuse)
|
|
99
|
+
"source_blocking", # must return to source collection (missing sources, stale, needs_recrawl)
|
|
100
|
+
"configuration_error", # user must fix config (no API key, missing search_tasks)
|
|
101
|
+
"rendering_error", # docx/markdown rendering issue
|
|
102
|
+
"safety_blocking", # investment advice, redaction risk — cannot ship
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
RepairOwner = Literal["editor", "analyst", "source", "configuration", "rendering", "safety"]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass
|
|
109
|
+
class AuditFinding:
|
|
110
|
+
finding_id: str
|
|
111
|
+
severity: AuditSeverity
|
|
112
|
+
finding_type: str
|
|
113
|
+
description: str
|
|
114
|
+
recommendation: str = ""
|
|
115
|
+
related_claim_id: str = ""
|
|
116
|
+
line_number: int | None = None
|
|
117
|
+
evidence: str = ""
|
|
118
|
+
blocking_level: BlockingLevel = "editor_fixable"
|
|
119
|
+
repair_owner: RepairOwner = "editor"
|
|
120
|
+
|
|
121
|
+
def to_dict(self) -> dict[str, Any]:
|
|
122
|
+
return asdict(self)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class AuditReport:
|
|
127
|
+
audit_status: AuditStatus
|
|
128
|
+
audit_score: int
|
|
129
|
+
findings: list[AuditFinding] = field(default_factory=list)
|
|
130
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
131
|
+
|
|
132
|
+
def to_dict(self) -> dict[str, Any]:
|
|
133
|
+
data = asdict(self)
|
|
134
|
+
data["findings"] = [finding.to_dict() for finding in self.findings]
|
|
135
|
+
return data
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass
|
|
139
|
+
class AgentOutput:
|
|
140
|
+
agent_name: str
|
|
141
|
+
summary: str
|
|
142
|
+
artifacts: dict[str, Any] = field(default_factory=dict)
|
|
143
|
+
created_at: str = field(default_factory=utc_now_iso)
|
|
144
|
+
|
|
145
|
+
def to_dict(self) -> dict[str, Any]:
|
|
146
|
+
return asdict(self)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass
|
|
150
|
+
class ReportState:
|
|
151
|
+
draft_markdown: str = ""
|
|
152
|
+
prepared_markdown: str = ""
|
|
153
|
+
sections: list[BriefSection] = field(default_factory=list)
|
|
154
|
+
audit_report: AuditReport | None = None
|
|
155
|
+
final_clean_report: dict[str, Any] | None = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class PipelineContext:
|
|
160
|
+
project_name: str
|
|
161
|
+
input_dir: str
|
|
162
|
+
output_dir: str
|
|
163
|
+
language: str = "en-US"
|
|
164
|
+
audience: str = "management"
|
|
165
|
+
audience_profile: str = "" # mapped profile ID (management, research, ir, legal_compliance, default)
|
|
166
|
+
report_date: str = ""
|
|
167
|
+
max_source_age_days: int | None = None
|
|
168
|
+
fail_on_stale_source: bool = False
|
|
169
|
+
previous_report_dir: str = ""
|
|
170
|
+
previous_report_text: str = ""
|
|
171
|
+
max_claims: int = 160
|
|
172
|
+
quiet_week_min_claims: int = 5
|
|
173
|
+
sources: list[SourceItem] = field(default_factory=list)
|
|
174
|
+
candidates: list[CandidateItem] = field(default_factory=list)
|
|
175
|
+
report_state: ReportState = field(default_factory=ReportState)
|
|
176
|
+
output_formats: list[str] = field(default_factory=lambda: ["markdown"])
|
|
177
|
+
output_footer: str = ""
|
|
178
|
+
output_filename_template: str = ""
|
|
179
|
+
output_filename_tokens: dict[str, str] = field(default_factory=dict)
|
|
180
|
+
output_named_outputs: bool = True
|
|
181
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|