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,189 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from multi_agent_brief.audit.deterministic import parse_date
|
|
7
|
+
from multi_agent_brief.core.claim_ledger import ClaimLedger
|
|
8
|
+
from multi_agent_brief.core.schemas import Claim, PipelineContext
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class SelectionResult:
|
|
13
|
+
selected: list[Claim]
|
|
14
|
+
excluded: list[dict] = field(default_factory=list)
|
|
15
|
+
stats: dict = field(default_factory=dict)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
TOPIC_CAPS = {
|
|
19
|
+
"policy": 24,
|
|
20
|
+
"compliance": 18,
|
|
21
|
+
"earnings": 24,
|
|
22
|
+
"competitor": 18,
|
|
23
|
+
"market": 24,
|
|
24
|
+
"demand": 18,
|
|
25
|
+
"rates": 12,
|
|
26
|
+
"capital": 14,
|
|
27
|
+
"technology": 12,
|
|
28
|
+
"general": 18,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def select_reportable_claims(
|
|
33
|
+
ledger: ClaimLedger,
|
|
34
|
+
context: PipelineContext,
|
|
35
|
+
previous_report_text: str = "",
|
|
36
|
+
) -> SelectionResult:
|
|
37
|
+
scored: list[tuple[str, int, Claim]] = []
|
|
38
|
+
excluded: list[dict] = []
|
|
39
|
+
|
|
40
|
+
for claim in ledger:
|
|
41
|
+
reason = exclusion_reason(claim, context)
|
|
42
|
+
if reason:
|
|
43
|
+
claim.metadata["excluded_reason"] = reason
|
|
44
|
+
excluded.append({"claim_id": claim.claim_id, "reason": reason, "statement": claim.statement})
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
topic = infer_topic(claim)
|
|
48
|
+
repeat = previous_report_topic_hit(claim_search_text(claim), previous_report_text)
|
|
49
|
+
score = novelty_score(claim, topic, repeat)
|
|
50
|
+
claim.metadata.update({"topic": topic, "repeat": repeat, "novelty_score": score})
|
|
51
|
+
scored.append((topic, score, claim))
|
|
52
|
+
|
|
53
|
+
selected: list[Claim] = []
|
|
54
|
+
omitted_by_topic: dict[str, int] = {}
|
|
55
|
+
for topic, cap in TOPIC_CAPS.items():
|
|
56
|
+
bucket = [item for item in scored if item[0] == topic]
|
|
57
|
+
bucket.sort(key=lambda item: item[1], reverse=True)
|
|
58
|
+
selected.extend(item[2] for item in bucket[:cap])
|
|
59
|
+
if len(bucket) > cap:
|
|
60
|
+
omitted_by_topic[topic] = len(bucket) - cap
|
|
61
|
+
|
|
62
|
+
selected.sort(key=lambda claim: int(claim.metadata.get("novelty_score", 0)), reverse=True)
|
|
63
|
+
selected = selected[: context.max_claims]
|
|
64
|
+
|
|
65
|
+
return SelectionResult(
|
|
66
|
+
selected=selected,
|
|
67
|
+
excluded=excluded,
|
|
68
|
+
stats={
|
|
69
|
+
"input_claims": len(ledger),
|
|
70
|
+
"selected_claims": len(selected),
|
|
71
|
+
"excluded_claims": len(excluded),
|
|
72
|
+
"omitted_by_topic": omitted_by_topic,
|
|
73
|
+
"repeat_claims": sum(1 for claim in selected if claim.metadata.get("repeat") is True),
|
|
74
|
+
"quiet_week": len(selected) < context.quiet_week_min_claims,
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def ledger_from_selected(result: SelectionResult) -> ClaimLedger:
|
|
80
|
+
return ClaimLedger(result.selected)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def exclusion_reason(claim: Claim, context: PipelineContext) -> str:
|
|
84
|
+
if claim.claim_type == "needs_recrawl":
|
|
85
|
+
return "needs_recrawl"
|
|
86
|
+
if str(claim.metadata.get("source_tier", "")) == "T5":
|
|
87
|
+
return "low_confidence_source"
|
|
88
|
+
report_day = parse_date(context.report_date)
|
|
89
|
+
published_day = parse_date(str(claim.metadata.get("published_at", "")))
|
|
90
|
+
if report_day and published_day and context.max_source_age_days is not None:
|
|
91
|
+
if (report_day - published_day).days > context.max_source_age_days:
|
|
92
|
+
return "stale_source"
|
|
93
|
+
return ""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def claim_search_text(claim: Claim) -> str:
|
|
97
|
+
return " ".join(
|
|
98
|
+
str(part or "")
|
|
99
|
+
for part in (
|
|
100
|
+
claim.claim_id,
|
|
101
|
+
claim.statement,
|
|
102
|
+
claim.evidence_text,
|
|
103
|
+
claim.source_id,
|
|
104
|
+
claim.source_url,
|
|
105
|
+
claim.claim_type,
|
|
106
|
+
claim.metadata.get("source_tier", ""),
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def compact_text_for_match(text: str) -> str:
|
|
112
|
+
return re.sub(r"[\W_]+", "", (text or "").lower(), flags=re.UNICODE)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def previous_report_topic_hit(text: str, previous_report_text: str) -> bool:
|
|
116
|
+
if not text or not previous_report_text:
|
|
117
|
+
return False
|
|
118
|
+
compact = compact_text_for_match(text)
|
|
119
|
+
previous = compact_text_for_match(previous_report_text)
|
|
120
|
+
if len(compact) >= 32 and (compact[:80] in previous or compact[:48] in previous):
|
|
121
|
+
return True
|
|
122
|
+
theme_groups = [
|
|
123
|
+
("adcvd", "antidumping", "countervailing"),
|
|
124
|
+
("feoc", "45x", "48e"),
|
|
125
|
+
("uflpa", "detain", "forcedlabor"),
|
|
126
|
+
("chapter11", "bankrupt", "residential"),
|
|
127
|
+
("section337", "topcon", "patent"),
|
|
128
|
+
]
|
|
129
|
+
for group in theme_groups:
|
|
130
|
+
hits = sum(1 for term in group if term in compact and term in previous)
|
|
131
|
+
if hits >= 2:
|
|
132
|
+
return True
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def infer_topic(claim: Claim) -> str:
|
|
137
|
+
text = claim_search_text(claim).lower()
|
|
138
|
+
if any(term in text for term in ("ad/cvd", "antidumping", "countervailing", "tariff", "section 337", "policy", "regulation")):
|
|
139
|
+
return "policy"
|
|
140
|
+
if any(term in text for term in ("uflpa", "cbp", "detain", "forced labor", "compliance")):
|
|
141
|
+
return "compliance"
|
|
142
|
+
if any(term in text for term in ("revenue", "eps", "ebitda", "gross margin", "earnings", "backlog", "guidance")):
|
|
143
|
+
return "earnings"
|
|
144
|
+
if any(term in text for term in ("competitor", "capacity", "expansion", "plant", "launch")):
|
|
145
|
+
return "competitor"
|
|
146
|
+
if any(term in text for term in ("price", "demand", "inventory", "market")):
|
|
147
|
+
return "market"
|
|
148
|
+
if any(term in text for term in ("installation", "generation", "ppa", "interconnection", "load growth")):
|
|
149
|
+
return "demand"
|
|
150
|
+
if any(term in text for term in ("treasury", "yield", "sofr", "fed", "rate")):
|
|
151
|
+
return "rates"
|
|
152
|
+
if any(term in text for term in ("acquisition", "investment", "fund", "capital")):
|
|
153
|
+
return "capital"
|
|
154
|
+
if any(term in text for term in ("topcon", "hjt", "bc", "technology", "efficiency")):
|
|
155
|
+
return "technology"
|
|
156
|
+
return "general"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def novelty_score(claim: Claim, topic: str, repeat: bool) -> int:
|
|
160
|
+
text = claim_search_text(claim).lower()
|
|
161
|
+
score = 10
|
|
162
|
+
tier = str(claim.metadata.get("source_tier", "") or "")
|
|
163
|
+
score += {"T1": 28, "T2": 24, "T3": 18, "T4": 14, "T5": 2}.get(tier, 8)
|
|
164
|
+
if claim.claim_type in {"number", "date"}:
|
|
165
|
+
score += 8
|
|
166
|
+
if claim.claim_type in {"risk", "forecast"}:
|
|
167
|
+
score += 6
|
|
168
|
+
high_signal_terms = [
|
|
169
|
+
"tariff",
|
|
170
|
+
"section 337",
|
|
171
|
+
"investigation",
|
|
172
|
+
"earnings",
|
|
173
|
+
"eps",
|
|
174
|
+
"ebitda",
|
|
175
|
+
"backlog",
|
|
176
|
+
"guidance",
|
|
177
|
+
"bankrupt",
|
|
178
|
+
"acquisition",
|
|
179
|
+
"capex",
|
|
180
|
+
"data center",
|
|
181
|
+
"storage",
|
|
182
|
+
]
|
|
183
|
+
score += sum(6 for term in high_signal_terms if term in text)
|
|
184
|
+
if topic in {"policy", "earnings", "market"}:
|
|
185
|
+
score += 4
|
|
186
|
+
if repeat:
|
|
187
|
+
score -= 35
|
|
188
|
+
return score
|
|
189
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class DeliveryArtifact:
|
|
10
|
+
path: str
|
|
11
|
+
title: str = ""
|
|
12
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class DeliveryTarget:
|
|
17
|
+
channel: str
|
|
18
|
+
recipient: str = ""
|
|
19
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class DeliveryResult:
|
|
24
|
+
connector_name: str
|
|
25
|
+
delivered: bool
|
|
26
|
+
message: str = ""
|
|
27
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DeliveryConnector(ABC):
|
|
31
|
+
name = "delivery-connector"
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def deliver(self, artifact: DeliveryArtifact, target: DeliveryTarget) -> DeliveryResult:
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Feishu/Lark delivery connector using lark-cli.
|
|
2
|
+
|
|
3
|
+
Sends briefs to Feishu via:
|
|
4
|
+
- IM message (lark-cli im +messages-send --as bot)
|
|
5
|
+
- Document creation (lark-cli docs +create)
|
|
6
|
+
- File upload (lark-cli drive +upload)
|
|
7
|
+
|
|
8
|
+
Requires lark-cli to be installed and authenticated.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from multi_agent_brief.delivery.base import DeliveryArtifact, DeliveryConnector, DeliveryResult, DeliveryTarget
|
|
19
|
+
|
|
20
|
+
# Max text length for IM messages (Feishu limit ~150k chars, conservative)
|
|
21
|
+
IM_MAX_LENGTH = 50000
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FeishuDeliveryConnector(DeliveryConnector):
|
|
25
|
+
"""Deliver brief artifacts to Feishu via lark-cli.
|
|
26
|
+
|
|
27
|
+
Supported channels:
|
|
28
|
+
- chat: send as text message to a group or user chat
|
|
29
|
+
- doc: create a Feishu document with the brief content
|
|
30
|
+
- drive: upload the brief file to Feishu Drive
|
|
31
|
+
|
|
32
|
+
Target recipient format:
|
|
33
|
+
- chat: chat_id (oc_xxx) or user open_id (ou_xxx)
|
|
34
|
+
- doc: folder_token (optional, defaults to root)
|
|
35
|
+
- drive: folder_token (optional, defaults to root)
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
name = "feishu"
|
|
39
|
+
|
|
40
|
+
def deliver(self, artifact: DeliveryArtifact, target: DeliveryTarget) -> DeliveryResult:
|
|
41
|
+
"""Deliver a brief artifact to a Feishu target.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
artifact: The brief file to deliver (path + title).
|
|
45
|
+
target:
|
|
46
|
+
- channel='chat', recipient='oc_xxx' (chat_id) or 'ou_xxx' (user_id)
|
|
47
|
+
- channel='doc', recipient='folder_token' (optional)
|
|
48
|
+
- channel='drive', recipient='folder_token' (optional)
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
DeliveryResult with delivery status and message.
|
|
52
|
+
"""
|
|
53
|
+
# Check prerequisites
|
|
54
|
+
if not shutil.which("lark-cli"):
|
|
55
|
+
return DeliveryResult(
|
|
56
|
+
self.name, False,
|
|
57
|
+
"feishu: 'lark-cli' not found. Install: npx @larksuite/cli@latest install",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
path = Path(artifact.path)
|
|
61
|
+
if not path.exists():
|
|
62
|
+
return DeliveryResult(self.name, False, f"feishu: artifact not found: {artifact.path}")
|
|
63
|
+
|
|
64
|
+
title = artifact.title or path.stem
|
|
65
|
+
|
|
66
|
+
channel = target.channel or "chat"
|
|
67
|
+
|
|
68
|
+
if channel == "chat":
|
|
69
|
+
return self._deliver_chat(path, title, target)
|
|
70
|
+
elif channel == "doc":
|
|
71
|
+
return self._deliver_doc(path, title, target)
|
|
72
|
+
elif channel == "drive":
|
|
73
|
+
return self._deliver_drive(path, title, target)
|
|
74
|
+
else:
|
|
75
|
+
return DeliveryResult(
|
|
76
|
+
self.name, False,
|
|
77
|
+
f"feishu: unknown channel '{channel}'. Supported: chat, doc, drive",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def _check_auth(self) -> str | None:
|
|
81
|
+
"""Check lark-cli auth status. Returns error message or None if OK."""
|
|
82
|
+
try:
|
|
83
|
+
result = subprocess.run(
|
|
84
|
+
["lark-cli", "auth", "status"],
|
|
85
|
+
capture_output=True, text=True, timeout=10,
|
|
86
|
+
)
|
|
87
|
+
if result.returncode != 0:
|
|
88
|
+
return "feishu: not authenticated. Run: lark-cli auth login --recommend"
|
|
89
|
+
return None
|
|
90
|
+
except (FileNotFoundError, subprocess.TimeoutExpired) as e:
|
|
91
|
+
return f"feishu: unable to check auth: {e}"
|
|
92
|
+
|
|
93
|
+
def _run_lark_cli(self, args: list[str]) -> subprocess.CompletedProcess | None:
|
|
94
|
+
"""Run lark-cli and return result.
|
|
95
|
+
|
|
96
|
+
Disables proxy (LARK_CLI_NO_PROXY=1) so lark-cli does not route
|
|
97
|
+
credentials through HTTPS_PROXY. Also resolves any file path
|
|
98
|
+
arguments to absolute paths to avoid lark-cli's cwd-traversal guard.
|
|
99
|
+
"""
|
|
100
|
+
env = {**os.environ, "LARK_CLI_NO_PROXY": "1"}
|
|
101
|
+
# Resolve --file paths to absolute to avoid lark-cli rejecting ".."
|
|
102
|
+
resolved_args: list[str] = []
|
|
103
|
+
skip_next = False
|
|
104
|
+
for i, arg in enumerate(args):
|
|
105
|
+
if skip_next:
|
|
106
|
+
resolved_args.append(arg)
|
|
107
|
+
skip_next = False
|
|
108
|
+
continue
|
|
109
|
+
if arg == "--file" and i + 1 < len(args):
|
|
110
|
+
resolved_args.append("--file")
|
|
111
|
+
resolved_args.append(str(Path(args[i + 1]).resolve()))
|
|
112
|
+
skip_next = True
|
|
113
|
+
else:
|
|
114
|
+
resolved_args.append(arg)
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
return subprocess.run(
|
|
118
|
+
["lark-cli"] + resolved_args,
|
|
119
|
+
capture_output=True, text=True, timeout=60, env=env,
|
|
120
|
+
)
|
|
121
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
def _deliver_chat(self, path: Path, title: str, target: DeliveryTarget) -> DeliveryResult:
|
|
125
|
+
"""Send brief content as IM message."""
|
|
126
|
+
auth_err = self._check_auth()
|
|
127
|
+
if auth_err:
|
|
128
|
+
return DeliveryResult(self.name, False, auth_err)
|
|
129
|
+
|
|
130
|
+
chat_id = target.recipient or target.metadata.get("chat_id", "")
|
|
131
|
+
if not chat_id:
|
|
132
|
+
return DeliveryResult(self.name, False, "feishu (chat): no chat_id provided")
|
|
133
|
+
|
|
134
|
+
# Read file content (truncate for IM limits)
|
|
135
|
+
try:
|
|
136
|
+
content = path.read_text(encoding="utf-8")[:IM_MAX_LENGTH]
|
|
137
|
+
except Exception as e:
|
|
138
|
+
return DeliveryResult(self.name, False, f"feishu (chat): failed to read artifact: {e}")
|
|
139
|
+
|
|
140
|
+
result = self._run_lark_cli([
|
|
141
|
+
"im", "+messages-send",
|
|
142
|
+
"--chat-id", chat_id,
|
|
143
|
+
"--as", "bot",
|
|
144
|
+
"--text", content,
|
|
145
|
+
])
|
|
146
|
+
if result is None or result.returncode != 0:
|
|
147
|
+
stderr = (result.stderr or "").strip() if result else "command failed"
|
|
148
|
+
return DeliveryResult(self.name, False, f"feishu (chat): {stderr[:200]}")
|
|
149
|
+
|
|
150
|
+
return DeliveryResult(self.name, True, f"Sent to chat {chat_id}")
|
|
151
|
+
|
|
152
|
+
def _deliver_doc(self, path: Path, title: str, target: DeliveryTarget) -> DeliveryResult:
|
|
153
|
+
"""Create a Feishu document from brief content."""
|
|
154
|
+
auth_err = self._check_auth()
|
|
155
|
+
if auth_err:
|
|
156
|
+
return DeliveryResult(self.name, False, auth_err)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
content = path.read_text(encoding="utf-8")
|
|
160
|
+
except Exception as e:
|
|
161
|
+
return DeliveryResult(self.name, False, f"feishu (doc): failed to read artifact: {e}")
|
|
162
|
+
|
|
163
|
+
# Build doc content with title
|
|
164
|
+
doc_content = f"<title>{title}</title>\n{content}"
|
|
165
|
+
|
|
166
|
+
result = self._run_lark_cli([
|
|
167
|
+
"docs", "+create",
|
|
168
|
+
"--api-version", "v2",
|
|
169
|
+
"--doc-format", "markdown",
|
|
170
|
+
"--content", doc_content,
|
|
171
|
+
])
|
|
172
|
+
if result is None or result.returncode != 0:
|
|
173
|
+
stderr = (result.stderr or "").strip()[:200] if result else "command failed"
|
|
174
|
+
return DeliveryResult(self.name, False, f"feishu (doc): {stderr}")
|
|
175
|
+
|
|
176
|
+
# Try to extract the doc URL from output
|
|
177
|
+
try:
|
|
178
|
+
output = json.loads(result.stdout) if result.stdout else {}
|
|
179
|
+
doc_url = output.get("url", output.get("data", {}).get("url", ""))
|
|
180
|
+
except (json.JSONDecodeError, AttributeError):
|
|
181
|
+
doc_url = ""
|
|
182
|
+
|
|
183
|
+
msg = f"Doc created: {title}"
|
|
184
|
+
if doc_url:
|
|
185
|
+
msg += f" ({doc_url})"
|
|
186
|
+
return DeliveryResult(self.name, True, msg, metadata={"url": doc_url})
|
|
187
|
+
|
|
188
|
+
def _deliver_drive(self, path: Path, title: str, target: DeliveryTarget) -> DeliveryResult:
|
|
189
|
+
"""Upload brief file to Feishu Drive."""
|
|
190
|
+
auth_err = self._check_auth()
|
|
191
|
+
if auth_err:
|
|
192
|
+
return DeliveryResult(self.name, False, auth_err)
|
|
193
|
+
|
|
194
|
+
folder_token = target.recipient or ""
|
|
195
|
+
args = ["drive", "+upload", "--file", str(path)]
|
|
196
|
+
if folder_token:
|
|
197
|
+
args.extend(["--folder-token", folder_token])
|
|
198
|
+
|
|
199
|
+
result = self._run_lark_cli(args)
|
|
200
|
+
if result is None or result.returncode != 0:
|
|
201
|
+
stderr = (result.stderr or "").strip()[:200] if result else "command failed"
|
|
202
|
+
return DeliveryResult(self.name, False, f"feishu (drive): {stderr}")
|
|
203
|
+
|
|
204
|
+
return DeliveryResult(self.name, True, f"Uploaded {path.name} to Drive")
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Gmail delivery connector using the optional gws CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from multi_agent_brief.delivery.base import DeliveryArtifact, DeliveryConnector, DeliveryResult, DeliveryTarget
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class _GwsTimeout:
|
|
18
|
+
args: tuple[str, ...]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GwsGmailDeliveryConnector(DeliveryConnector):
|
|
22
|
+
"""Create Gmail drafts or send Gmail messages through the optional gws CLI."""
|
|
23
|
+
|
|
24
|
+
name = "gmail"
|
|
25
|
+
|
|
26
|
+
def deliver(self, artifact: DeliveryArtifact, target: DeliveryTarget) -> DeliveryResult:
|
|
27
|
+
if target.channel not in {"draft", "send"}:
|
|
28
|
+
return DeliveryResult(self.name, False, "gmail: supported channels are 'draft' and 'send'")
|
|
29
|
+
if not target.recipient:
|
|
30
|
+
return DeliveryResult(self.name, False, "gmail: --recipient is required")
|
|
31
|
+
|
|
32
|
+
path = Path(artifact.path)
|
|
33
|
+
if not path.exists():
|
|
34
|
+
return DeliveryResult(self.name, False, f"gmail: artifact not found: {artifact.path}")
|
|
35
|
+
|
|
36
|
+
if not shutil.which("gws"):
|
|
37
|
+
return DeliveryResult(
|
|
38
|
+
self.name,
|
|
39
|
+
False,
|
|
40
|
+
"gmail: 'gws' not found. Install googleworkspace/cli and run gws auth setup/login.",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
auth_error = self._check_auth()
|
|
44
|
+
if auth_error:
|
|
45
|
+
return DeliveryResult(self.name, False, auth_error)
|
|
46
|
+
|
|
47
|
+
subject = _metadata_text(target.metadata.get("subject")) or artifact.title or path.stem
|
|
48
|
+
body = _metadata_text(target.metadata.get("body")) or "Please review the attached BriefLoop delivery."
|
|
49
|
+
attachments = _metadata_list(target.metadata.get("attachments")) or [str(path)]
|
|
50
|
+
attachment_paths = [Path(attachment).expanduser().resolve() for attachment in attachments]
|
|
51
|
+
if any(not attachment.exists() for attachment in attachment_paths):
|
|
52
|
+
return DeliveryResult(self.name, False, "gmail: attachment not found")
|
|
53
|
+
attachment_cwd = Path(os.path.commonpath([str(attachment.parent) for attachment in attachment_paths]))
|
|
54
|
+
|
|
55
|
+
args = [
|
|
56
|
+
"gmail",
|
|
57
|
+
"+send",
|
|
58
|
+
"--to",
|
|
59
|
+
target.recipient,
|
|
60
|
+
"--subject",
|
|
61
|
+
subject,
|
|
62
|
+
"--body",
|
|
63
|
+
body,
|
|
64
|
+
]
|
|
65
|
+
if target.channel == "draft":
|
|
66
|
+
args.append("--draft")
|
|
67
|
+
for attachment in attachment_paths:
|
|
68
|
+
args.extend(["--attach", attachment.relative_to(attachment_cwd).as_posix()])
|
|
69
|
+
|
|
70
|
+
result = self._run_gws(args, cwd=attachment_cwd)
|
|
71
|
+
if isinstance(result, _GwsTimeout):
|
|
72
|
+
action = "draft creation" if target.channel == "draft" else "send"
|
|
73
|
+
inspect_target = "Gmail Drafts" if target.channel == "draft" else "Gmail Sent Mail"
|
|
74
|
+
return DeliveryResult(
|
|
75
|
+
self.name,
|
|
76
|
+
False,
|
|
77
|
+
f"gmail: gws {action} timed out after the Gmail request may have been accepted. "
|
|
78
|
+
f"Inspect {inspect_target} before retrying; do not retry blindly.",
|
|
79
|
+
metadata={
|
|
80
|
+
"outcome_unknown": True,
|
|
81
|
+
"timeout": True,
|
|
82
|
+
"inspect_target": inspect_target,
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
if result is None:
|
|
86
|
+
return DeliveryResult(self.name, False, "gmail: gws command failed")
|
|
87
|
+
if result.returncode != 0:
|
|
88
|
+
action = "draft creation" if target.channel == "draft" else "send"
|
|
89
|
+
return DeliveryResult(
|
|
90
|
+
self.name,
|
|
91
|
+
False,
|
|
92
|
+
f"gmail: gws {action} failed. Check gws auth, Gmail permissions, recipient, and attachment access.",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if target.channel == "draft":
|
|
96
|
+
metadata = _draft_metadata(result.stdout)
|
|
97
|
+
if not metadata.get("draft_id_present"):
|
|
98
|
+
return DeliveryResult(
|
|
99
|
+
self.name,
|
|
100
|
+
False,
|
|
101
|
+
"gmail: gws did not confirm Gmail draft creation. "
|
|
102
|
+
"Inspect Gmail Drafts before retrying; do not retry blindly.",
|
|
103
|
+
)
|
|
104
|
+
return DeliveryResult(
|
|
105
|
+
self.name,
|
|
106
|
+
True,
|
|
107
|
+
"Gmail draft created",
|
|
108
|
+
metadata=metadata,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
metadata = _message_metadata(result.stdout)
|
|
112
|
+
if not metadata.get("sent_message_present"):
|
|
113
|
+
return DeliveryResult(
|
|
114
|
+
self.name,
|
|
115
|
+
False,
|
|
116
|
+
"gmail: gws did not confirm Gmail send. "
|
|
117
|
+
"Inspect Gmail Sent Mail before retrying; do not retry blindly.",
|
|
118
|
+
)
|
|
119
|
+
return DeliveryResult(
|
|
120
|
+
self.name,
|
|
121
|
+
True,
|
|
122
|
+
"Gmail message sent",
|
|
123
|
+
metadata=metadata,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def _check_auth(self) -> str | None:
|
|
127
|
+
result = self._run_gws(["auth", "status"], timeout=10)
|
|
128
|
+
if result is None or isinstance(result, _GwsTimeout):
|
|
129
|
+
return "gmail: unable to check gws auth. Run: gws auth setup; gws auth login"
|
|
130
|
+
if result.returncode != 0:
|
|
131
|
+
if _has_external_auth_signal():
|
|
132
|
+
return None
|
|
133
|
+
return "gmail: gws is not authenticated. Run: gws auth setup; gws auth login"
|
|
134
|
+
payload = _json_object_from_output(result.stdout)
|
|
135
|
+
if payload is None:
|
|
136
|
+
return None
|
|
137
|
+
if isinstance(payload, dict) and payload.get("auth_method") == "none" and not _has_external_auth_signal():
|
|
138
|
+
return "gmail: gws is not authenticated. Run: gws auth setup; gws auth login"
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
def _run_gws(
|
|
142
|
+
self,
|
|
143
|
+
args: list[str],
|
|
144
|
+
*,
|
|
145
|
+
timeout: int = 60,
|
|
146
|
+
cwd: Path | None = None,
|
|
147
|
+
) -> subprocess.CompletedProcess[str] | _GwsTimeout | None:
|
|
148
|
+
env = {**os.environ}
|
|
149
|
+
try:
|
|
150
|
+
return subprocess.run(
|
|
151
|
+
["gws", *args],
|
|
152
|
+
capture_output=True,
|
|
153
|
+
text=True,
|
|
154
|
+
timeout=timeout,
|
|
155
|
+
cwd=str(cwd) if cwd is not None else None,
|
|
156
|
+
env=env,
|
|
157
|
+
)
|
|
158
|
+
except FileNotFoundError:
|
|
159
|
+
return None
|
|
160
|
+
except subprocess.TimeoutExpired:
|
|
161
|
+
return _GwsTimeout(tuple(args))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _metadata_text(value: Any) -> str:
|
|
165
|
+
return value.strip() if isinstance(value, str) and value.strip() else ""
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _metadata_list(value: Any) -> list[str]:
|
|
169
|
+
if not isinstance(value, list):
|
|
170
|
+
return []
|
|
171
|
+
return [str(item) for item in value if str(item).strip()]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _has_external_auth_signal() -> bool:
|
|
175
|
+
if any(
|
|
176
|
+
os.environ.get(name)
|
|
177
|
+
for name in (
|
|
178
|
+
"GOOGLE_WORKSPACE_CLI_TOKEN",
|
|
179
|
+
"GWS_TOKEN",
|
|
180
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
181
|
+
)
|
|
182
|
+
):
|
|
183
|
+
return True
|
|
184
|
+
return any(path.exists() for path in _well_known_adc_paths())
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _well_known_adc_paths() -> list[Path]:
|
|
188
|
+
paths: list[Path] = []
|
|
189
|
+
cloudsdk_config = os.environ.get("CLOUDSDK_CONFIG")
|
|
190
|
+
if cloudsdk_config:
|
|
191
|
+
paths.append(Path(cloudsdk_config).expanduser() / "application_default_credentials.json")
|
|
192
|
+
paths.append(Path.home() / ".config" / "gcloud" / "application_default_credentials.json")
|
|
193
|
+
appdata = os.environ.get("APPDATA")
|
|
194
|
+
if appdata:
|
|
195
|
+
paths.append(Path(appdata).expanduser() / "gcloud" / "application_default_credentials.json")
|
|
196
|
+
return paths
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _json_object_from_output(stdout: str) -> dict[str, Any] | None:
|
|
200
|
+
text = stdout.strip()
|
|
201
|
+
if not text:
|
|
202
|
+
return {}
|
|
203
|
+
try:
|
|
204
|
+
payload = json.loads(text)
|
|
205
|
+
except json.JSONDecodeError:
|
|
206
|
+
start = text.find("{")
|
|
207
|
+
end = text.rfind("}")
|
|
208
|
+
if start < 0 or end < start:
|
|
209
|
+
return None
|
|
210
|
+
try:
|
|
211
|
+
payload = json.loads(text[start : end + 1])
|
|
212
|
+
except json.JSONDecodeError:
|
|
213
|
+
return None
|
|
214
|
+
return payload if isinstance(payload, dict) else None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _draft_metadata(stdout: str) -> dict[str, Any]:
|
|
218
|
+
payload = _json_object_from_output(stdout)
|
|
219
|
+
if payload is None:
|
|
220
|
+
return {}
|
|
221
|
+
data = payload.get("data") if isinstance(payload.get("data"), dict) else {}
|
|
222
|
+
draft_id = payload.get("id") or payload.get("draft_id") or data.get("id")
|
|
223
|
+
if isinstance(draft_id, str) and draft_id.strip():
|
|
224
|
+
return {"draft_id_present": True}
|
|
225
|
+
return {}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _message_metadata(stdout: str) -> dict[str, Any]:
|
|
229
|
+
payload = _json_object_from_output(stdout)
|
|
230
|
+
if payload is None:
|
|
231
|
+
return {}
|
|
232
|
+
data = payload.get("data") if isinstance(payload.get("data"), dict) else {}
|
|
233
|
+
message = payload.get("message") if isinstance(payload.get("message"), dict) else {}
|
|
234
|
+
sent_ref = payload.get("message" + "_id") or payload.get("id") or message.get("id") or data.get("id")
|
|
235
|
+
if isinstance(sent_ref, str) and sent_ref.strip():
|
|
236
|
+
return {"sent_message_present": True}
|
|
237
|
+
return {}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Public-safe evaluation case runner for MABW control surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from multi_agent_brief.evaluation_cases.runner import (
|
|
6
|
+
list_evaluation_cases,
|
|
7
|
+
run_evaluation_cases,
|
|
8
|
+
validate_evaluation_cases,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"list_evaluation_cases",
|
|
13
|
+
"run_evaluation_cases",
|
|
14
|
+
"validate_evaluation_cases",
|
|
15
|
+
]
|