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.
Files changed (424) hide show
  1. briefloop-0.11.12.dist-info/METADATA +473 -0
  2. briefloop-0.11.12.dist-info/RECORD +424 -0
  3. briefloop-0.11.12.dist-info/WHEEL +5 -0
  4. briefloop-0.11.12.dist-info/entry_points.txt +3 -0
  5. briefloop-0.11.12.dist-info/licenses/LICENSE +22 -0
  6. briefloop-0.11.12.dist-info/top_level.txt +1 -0
  7. multi_agent_brief/__init__.py +36 -0
  8. multi_agent_brief/analysis_blocks/__init__.py +11 -0
  9. multi_agent_brief/analysis_blocks/builder.py +206 -0
  10. multi_agent_brief/analysis_blocks/renderer.py +237 -0
  11. multi_agent_brief/analysis_blocks/schemas.py +52 -0
  12. multi_agent_brief/analysis_modules/__init__.py +8 -0
  13. multi_agent_brief/analysis_modules/base.py +87 -0
  14. multi_agent_brief/analysis_modules/market_competitor/__init__.py +68 -0
  15. multi_agent_brief/analysis_modules/market_competitor/auditor.py +290 -0
  16. multi_agent_brief/analysis_modules/market_competitor/config.py +187 -0
  17. multi_agent_brief/analysis_modules/market_competitor/event_builder.py +226 -0
  18. multi_agent_brief/analysis_modules/market_competitor/renderer.py +199 -0
  19. multi_agent_brief/analysis_modules/market_competitor/schemas.py +518 -0
  20. multi_agent_brief/analysis_modules/policy_regulatory/__init__.py +7 -0
  21. multi_agent_brief/analysis_modules/policy_regulatory/audit.py +195 -0
  22. multi_agent_brief/analysis_modules/policy_regulatory/module.py +387 -0
  23. multi_agent_brief/analysis_modules/policy_regulatory/schemas.py +129 -0
  24. multi_agent_brief/analysis_modules/registry.py +73 -0
  25. multi_agent_brief/audience/__init__.py +19 -0
  26. multi_agent_brief/audience/profiles.py +246 -0
  27. multi_agent_brief/audience_memory/__init__.py +23 -0
  28. multi_agent_brief/audience_memory/profile.py +358 -0
  29. multi_agent_brief/audit/__init__.py +36 -0
  30. multi_agent_brief/audit/case_applicability.py +181 -0
  31. multi_agent_brief/audit/deterministic.py +471 -0
  32. multi_agent_brief/audit/editorial_governance.py +404 -0
  33. multi_agent_brief/audit/final_quality.py +764 -0
  34. multi_agent_brief/audit/harness.py +242 -0
  35. multi_agent_brief/audit/interfaces.py +96 -0
  36. multi_agent_brief/audit/limitation_hygiene.py +238 -0
  37. multi_agent_brief/audit/proposal_boundary.py +34 -0
  38. multi_agent_brief/audit/redaction.py +27 -0
  39. multi_agent_brief/audit/rule_packs.py +96 -0
  40. multi_agent_brief/audit/semantic.py +435 -0
  41. multi_agent_brief/capabilities/__init__.py +22 -0
  42. multi_agent_brief/capabilities/catalog.py +215 -0
  43. multi_agent_brief/capabilities/detect.py +199 -0
  44. multi_agent_brief/capabilities/models.py +60 -0
  45. multi_agent_brief/capabilities/recommend.py +206 -0
  46. multi_agent_brief/cli/__init__.py +2 -0
  47. multi_agent_brief/cli/analysis_commands.py +140 -0
  48. multi_agent_brief/cli/approval_commands.py +107 -0
  49. multi_agent_brief/cli/audit_commands.py +59 -0
  50. multi_agent_brief/cli/capability_commands.py +548 -0
  51. multi_agent_brief/cli/claude_commands.py +159 -0
  52. multi_agent_brief/cli/competitors_commands.py +182 -0
  53. multi_agent_brief/cli/controls_commands.py +137 -0
  54. multi_agent_brief/cli/deliver_commands.py +942 -0
  55. multi_agent_brief/cli/eval_cases_commands.py +99 -0
  56. multi_agent_brief/cli/experiments_commands.py +395 -0
  57. multi_agent_brief/cli/feedback_commands.py +192 -0
  58. multi_agent_brief/cli/finalize_commands.py +153 -0
  59. multi_agent_brief/cli/gates_commands.py +174 -0
  60. multi_agent_brief/cli/hermes_commands.py +356 -0
  61. multi_agent_brief/cli/improve_commands.py +224 -0
  62. multi_agent_brief/cli/init_commands.py +564 -0
  63. multi_agent_brief/cli/init_wizard.py +1473 -0
  64. multi_agent_brief/cli/input_commands.py +237 -0
  65. multi_agent_brief/cli/main.py +288 -0
  66. multi_agent_brief/cli/onboard_commands.py +193 -0
  67. multi_agent_brief/cli/product_commands.py +1449 -0
  68. multi_agent_brief/cli/provenance_commands.py +100 -0
  69. multi_agent_brief/cli/release_commands.py +68 -0
  70. multi_agent_brief/cli/repair_commands.py +169 -0
  71. multi_agent_brief/cli/run_commands.py +292 -0
  72. multi_agent_brief/cli/runtime_commands.py +81 -0
  73. multi_agent_brief/cli/secrets_commands.py +232 -0
  74. multi_agent_brief/cli/semantic_support_commands.py +80 -0
  75. multi_agent_brief/cli/sources_commands.py +647 -0
  76. multi_agent_brief/cli/start_commands.py +8 -0
  77. multi_agent_brief/cli/state_commands.py +446 -0
  78. multi_agent_brief/cli/status_commands.py +26 -0
  79. multi_agent_brief/cli/workbuddy_commands.py +103 -0
  80. multi_agent_brief/configs/artifact_contracts.yaml +514 -0
  81. multi_agent_brief/configs/orchestrator_contract.yaml +181 -0
  82. multi_agent_brief/configs/policy_packs/default.yaml +65 -0
  83. multi_agent_brief/configs/policy_profiles/evidence_extract_default.yaml +58 -0
  84. multi_agent_brief/configs/policy_profiles/finance_default.yaml +44 -0
  85. multi_agent_brief/configs/policy_profiles/internet_default.yaml +44 -0
  86. multi_agent_brief/configs/policy_profiles/manufacturing_default.yaml +33 -0
  87. multi_agent_brief/configs/policy_profiles/solar_manufacturing_default.yaml +64 -0
  88. multi_agent_brief/configs/report_packs/evidence_extract.yaml +48 -0
  89. multi_agent_brief/configs/report_packs/management_monthly.yaml +36 -0
  90. multi_agent_brief/configs/report_packs/market_weekly.yaml +36 -0
  91. multi_agent_brief/configs/report_packs/solar_industry_periodic.yaml +50 -0
  92. multi_agent_brief/configs/report_templates/evidence_extract.yaml +55 -0
  93. multi_agent_brief/configs/report_templates/management_monthly.yaml +48 -0
  94. multi_agent_brief/configs/report_templates/market_weekly.yaml +49 -0
  95. multi_agent_brief/configs/report_templates/solar_industry_periodic.yaml +75 -0
  96. multi_agent_brief/configs/stage_specs.yaml +180 -0
  97. multi_agent_brief/contracts/__init__.py +41 -0
  98. multi_agent_brief/contracts/base.py +99 -0
  99. multi_agent_brief/contracts/errors.py +41 -0
  100. multi_agent_brief/contracts/migrations/__init__.py +5 -0
  101. multi_agent_brief/contracts/migrations/claim_v1_to_v2.py +33 -0
  102. multi_agent_brief/contracts/registry.py +159 -0
  103. multi_agent_brief/contracts/role_topology.py +25 -0
  104. multi_agent_brief/contracts/schemas/__init__.py +35 -0
  105. multi_agent_brief/contracts/schemas/analysis_pack.py +135 -0
  106. multi_agent_brief/contracts/schemas/atomic_claim_graph.py +304 -0
  107. multi_agent_brief/contracts/schemas/audit_report.py +80 -0
  108. multi_agent_brief/contracts/schemas/candidate_item.py +51 -0
  109. multi_agent_brief/contracts/schemas/claim.py +197 -0
  110. multi_agent_brief/contracts/schemas/claim_draft.py +386 -0
  111. multi_agent_brief/contracts/schemas/claim_support_matrix.py +334 -0
  112. multi_agent_brief/contracts/schemas/evidence_span_registry.py +270 -0
  113. multi_agent_brief/contracts/schemas/policy_profile.py +247 -0
  114. multi_agent_brief/contracts/schemas/report_spec.py +257 -0
  115. multi_agent_brief/contracts/schemas/semantic_assessment_report.py +480 -0
  116. multi_agent_brief/contracts/schemas/source_evidence_pack_manifest.py +233 -0
  117. multi_agent_brief/contracts/schemas/source_item.py +96 -0
  118. multi_agent_brief/contracts/semantic_assessment_status.py +12 -0
  119. multi_agent_brief/contracts/source_metadata.py +252 -0
  120. multi_agent_brief/contracts/target_contract.py +460 -0
  121. multi_agent_brief/contracts/validator.py +225 -0
  122. multi_agent_brief/controls/__init__.py +23 -0
  123. multi_agent_brief/controls/contract.py +106 -0
  124. multi_agent_brief/controls/switchboard.py +1076 -0
  125. multi_agent_brief/core/__init__.py +2 -0
  126. multi_agent_brief/core/citations.py +14 -0
  127. multi_agent_brief/core/claim_ledger.py +94 -0
  128. multi_agent_brief/core/config.py +191 -0
  129. multi_agent_brief/core/env.py +69 -0
  130. multi_agent_brief/core/previous.py +83 -0
  131. multi_agent_brief/core/schemas.py +181 -0
  132. multi_agent_brief/core/selection.py +189 -0
  133. multi_agent_brief/delivery/__init__.py +6 -0
  134. multi_agent_brief/delivery/base.py +36 -0
  135. multi_agent_brief/delivery/feishu.py +204 -0
  136. multi_agent_brief/delivery/gws.py +237 -0
  137. multi_agent_brief/evaluation_cases/__init__.py +15 -0
  138. multi_agent_brief/evaluation_cases/contract.py +429 -0
  139. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/config.yaml +18 -0
  140. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/improvement/ledger.jsonl +2 -0
  141. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/sources.yaml +2 -0
  142. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/user.md +4 -0
  143. multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/config.yaml +13 -0
  144. multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/sources.yaml +9 -0
  145. multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/user.md +4 -0
  146. multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/config.yaml +12 -0
  147. multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/sources.yaml +6 -0
  148. multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/user.md +4 -0
  149. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/config.yaml +6 -0
  150. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/input/human_feedback.md +1 -0
  151. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/sources.yaml +2 -0
  152. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/user.md +3 -0
  153. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/config.yaml +8 -0
  154. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/brief.md +14 -0
  155. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audit_report.json +1 -0
  156. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audited_brief.md +5 -0
  157. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/candidate_claims.json +1 -0
  158. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/claim_ledger.json +1 -0
  159. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/screened_candidates.json +1 -0
  160. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/sources.yaml +2 -0
  161. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/user.md +3 -0
  162. multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/config.yaml +13 -0
  163. multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/sources.yaml +9 -0
  164. multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/user.md +4 -0
  165. multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/config.yaml +4 -0
  166. multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/sources.yaml +2 -0
  167. multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/user.md +3 -0
  168. multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/config.yaml +13 -0
  169. multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/sources.yaml +9 -0
  170. multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/user.md +4 -0
  171. multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/config.yaml +13 -0
  172. multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/sources.yaml +9 -0
  173. multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/user.md +5 -0
  174. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/config.yaml +6 -0
  175. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/audited_brief.md +3 -0
  176. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/candidate_claims.json +1 -0
  177. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/claim_ledger.json +1 -0
  178. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/feedback_issues.json +25 -0
  179. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/repair_plan.json +27 -0
  180. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/screened_candidates.json +1 -0
  181. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/sources.yaml +2 -0
  182. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/user.md +3 -0
  183. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/config.yaml +8 -0
  184. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audit_report.json +1 -0
  185. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audited_brief.md +3 -0
  186. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/candidate_claims.json +1 -0
  187. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/claim_ledger.json +11 -0
  188. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/quality_gate_report.json +22 -0
  189. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/screened_candidates.json +1 -0
  190. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/sources.yaml +2 -0
  191. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/user.md +3 -0
  192. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/config.yaml +13 -0
  193. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audit_report.json +1 -0
  194. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audited_brief.md +5 -0
  195. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/candidate_claims.json +1 -0
  196. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/claim_ledger.json +40 -0
  197. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/screened_candidates.json +1 -0
  198. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/sources.yaml +3 -0
  199. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/user.md +3 -0
  200. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/config.yaml +8 -0
  201. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/brief.md +3 -0
  202. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audit_report.json +1 -0
  203. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audited_brief.md +3 -0
  204. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/candidate_claims.json +1 -0
  205. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/claim_ledger.json +13 -0
  206. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/screened_candidates.json +1 -0
  207. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/sources.yaml +2 -0
  208. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/user.md +3 -0
  209. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/config.yaml +13 -0
  210. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/output/intermediate/release_readiness_report.json +31 -0
  211. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/sources.yaml +9 -0
  212. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/user.md +4 -0
  213. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/config.yaml +18 -0
  214. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/ledger.jsonl +3 -0
  215. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/memory.md +3 -0
  216. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/output/intermediate/improvement_memory_snapshot.md +3 -0
  217. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/sources.yaml +2 -0
  218. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/user.md +4 -0
  219. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/config.yaml +12 -0
  220. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/input/sources/source-001.txt +1 -0
  221. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/brief.md +37 -0
  222. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/delivery/brief.md +37 -0
  223. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/atomic_claim_graph.json +17 -0
  224. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audit_report.json +5 -0
  225. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audited_brief.md +37 -0
  226. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/claim_ledger.json +20 -0
  227. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/evidence_span_registry.json +20 -0
  228. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/finalize_report.json +40 -0
  229. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/auditor_quality_gate_report.json +42 -0
  230. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/finalize_quality_gate_report.json +42 -0
  231. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/screened_candidates.json +33 -0
  232. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix.md +12 -0
  233. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix_trace.md +10 -0
  234. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/report_spec.yaml +24 -0
  235. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/sources.yaml +4 -0
  236. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/user.md +3 -0
  237. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/config.yaml +13 -0
  238. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/input/sources/README.md +4 -0
  239. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/output/intermediate/source_evidence_pack_manifest.json +26 -0
  240. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/sources.yaml +9 -0
  241. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/user.md +4 -0
  242. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/config.yaml +9 -0
  243. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audit_report.json +1 -0
  244. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audited_brief.md +3 -0
  245. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/candidate_claims.json +1 -0
  246. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/claim_ledger.json +13 -0
  247. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/screened_candidates.json +1 -0
  248. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/sources.yaml +2 -0
  249. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/user.md +3 -0
  250. multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/config.yaml +13 -0
  251. multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/sources.yaml +9 -0
  252. multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/user.md +4 -0
  253. multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/config.yaml +4 -0
  254. multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/sources.yaml +2 -0
  255. multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/user.md +3 -0
  256. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/config.yaml +18 -0
  257. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/improvement/ledger.jsonl +1 -0
  258. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/sources.yaml +2 -0
  259. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/user.md +4 -0
  260. multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/config.yaml +18 -0
  261. multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/sources.yaml +9 -0
  262. multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/user.md +5 -0
  263. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/config.yaml +8 -0
  264. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audit_report.json +1 -0
  265. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audited_brief.md +8 -0
  266. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/candidate_claims.json +1 -0
  267. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/claim_ledger.json +1 -0
  268. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/screened_candidates.json +1 -0
  269. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/sources.yaml +2 -0
  270. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/user.md +3 -0
  271. multi_agent_brief/evaluation_cases/fixtures/manifest.yaml +861 -0
  272. multi_agent_brief/evaluation_cases/fixtures.py +20 -0
  273. multi_agent_brief/evaluation_cases/runner.py +1603 -0
  274. multi_agent_brief/experiments/__init__.py +45 -0
  275. multi_agent_brief/experiments/experiment_080.py +5835 -0
  276. multi_agent_brief/experiments/schemas.py +63 -0
  277. multi_agent_brief/experiments/target_contract.py +10 -0
  278. multi_agent_brief/feedback/__init__.py +17 -0
  279. multi_agent_brief/feedback/feedback_contract.py +482 -0
  280. multi_agent_brief/feedback/feedback_state.py +898 -0
  281. multi_agent_brief/hermes/__init__.py +25 -0
  282. multi_agent_brief/hermes/adapter.py +832 -0
  283. multi_agent_brief/improvement/__init__.py +83 -0
  284. multi_agent_brief/improvement/contract.py +1114 -0
  285. multi_agent_brief/improvement/memory.py +451 -0
  286. multi_agent_brief/improvement/product_definition.py +228 -0
  287. multi_agent_brief/improvement/state.py +961 -0
  288. multi_agent_brief/inputs/__init__.py +2 -0
  289. multi_agent_brief/inputs/classifier.py +322 -0
  290. multi_agent_brief/inputs/contracts.py +59 -0
  291. multi_agent_brief/inputs/extractor.py +310 -0
  292. multi_agent_brief/install/__init__.py +2 -0
  293. multi_agent_brief/install/writer.py +97 -0
  294. multi_agent_brief/onboarding/__init__.py +11 -0
  295. multi_agent_brief/onboarding/io.py +103 -0
  296. multi_agent_brief/onboarding/mapper.py +484 -0
  297. multi_agent_brief/onboarding/schema.py +46 -0
  298. multi_agent_brief/orchestrator/__init__.py +2 -0
  299. multi_agent_brief/orchestrator/fact_layer_import.py +352 -0
  300. multi_agent_brief/orchestrator/handoff.py +2209 -0
  301. multi_agent_brief/orchestrator/role_topology.py +42 -0
  302. multi_agent_brief/orchestrator/run_archive.py +888 -0
  303. multi_agent_brief/orchestrator/run_integrity.py +346 -0
  304. multi_agent_brief/orchestrator/runtime_state/__init__.py +70 -0
  305. multi_agent_brief/orchestrator/runtime_state/_io.py +167 -0
  306. multi_agent_brief/orchestrator/runtime_state/_transactions.py +298 -0
  307. multi_agent_brief/orchestrator/runtime_state/artifact_registry.py +1825 -0
  308. multi_agent_brief/orchestrator/runtime_state/atomic_claim_graph.py +96 -0
  309. multi_agent_brief/orchestrator/runtime_state/claim_ledger_freeze.py +510 -0
  310. multi_agent_brief/orchestrator/runtime_state/claim_metadata_enrichment.py +836 -0
  311. multi_agent_brief/orchestrator/runtime_state/claim_support_matrix.py +582 -0
  312. multi_agent_brief/orchestrator/runtime_state/completion_gates.py +672 -0
  313. multi_agent_brief/orchestrator/runtime_state/contracts_loader.py +66 -0
  314. multi_agent_brief/orchestrator/runtime_state/decisions.py +239 -0
  315. multi_agent_brief/orchestrator/runtime_state/errors.py +61 -0
  316. multi_agent_brief/orchestrator/runtime_state/event_log.py +256 -0
  317. multi_agent_brief/orchestrator/runtime_state/evidence_span_registry.py +233 -0
  318. multi_agent_brief/orchestrator/runtime_state/fact_layer.py +755 -0
  319. multi_agent_brief/orchestrator/runtime_state/identity.py +90 -0
  320. multi_agent_brief/orchestrator/runtime_state/lifecycle.py +675 -0
  321. multi_agent_brief/orchestrator/runtime_state/manifest.py +82 -0
  322. multi_agent_brief/orchestrator/runtime_state/operations.py +224 -0
  323. multi_agent_brief/orchestrator/runtime_state/paths.py +38 -0
  324. multi_agent_brief/orchestrator/runtime_state/repair.py +984 -0
  325. multi_agent_brief/orchestrator/runtime_state/safety_surfaces.py +147 -0
  326. multi_agent_brief/orchestrator/runtime_state/semantic_assessment_report.py +692 -0
  327. multi_agent_brief/orchestrator/runtime_state/semantic_support_acceptance.py +683 -0
  328. multi_agent_brief/orchestrator/runtime_state/source_evidence_pack.py +131 -0
  329. multi_agent_brief/orchestrator/runtime_state/stage_completion.py +1320 -0
  330. multi_agent_brief/orchestrator/runtime_state/trajectory.py +200 -0
  331. multi_agent_brief/orchestrator/runtime_state/workflow.py +350 -0
  332. multi_agent_brief/orchestrator/source_evidence.py +43 -0
  333. multi_agent_brief/orchestrator/timing.py +410 -0
  334. multi_agent_brief/orchestrator_contract.py +96 -0
  335. multi_agent_brief/outputs/__init__.py +2 -0
  336. multi_agent_brief/outputs/atomic_reader_projection.py +288 -0
  337. multi_agent_brief/outputs/docx.py +32 -0
  338. multi_agent_brief/outputs/finalize.py +1036 -0
  339. multi_agent_brief/outputs/ib_docx.py +983 -0
  340. multi_agent_brief/outputs/naming.py +38 -0
  341. multi_agent_brief/outputs/pdf.py +20 -0
  342. multi_agent_brief/outputs/reader_final_gate.py +509 -0
  343. multi_agent_brief/outputs/reader_projection.py +397 -0
  344. multi_agent_brief/outputs/source_appendix.py +753 -0
  345. multi_agent_brief/outputs/templates/__init__.py +112 -0
  346. multi_agent_brief/product/__init__.py +49 -0
  347. multi_agent_brief/product/bundle_projection.py +686 -0
  348. multi_agent_brief/product/citation_profile.py +145 -0
  349. multi_agent_brief/product/guidance_manifestation.py +421 -0
  350. multi_agent_brief/product/materiality_selection.py +472 -0
  351. multi_agent_brief/product/policy_gate_adapter.py +116 -0
  352. multi_agent_brief/product/policy_profile.py +39 -0
  353. multi_agent_brief/product/policy_projection.py +161 -0
  354. multi_agent_brief/product/policy_registry.py +83 -0
  355. multi_agent_brief/product/policy_resolver.py +225 -0
  356. multi_agent_brief/product/quality_closeout.py +188 -0
  357. multi_agent_brief/product/quality_panel.py +2217 -0
  358. multi_agent_brief/product/release_approval.py +1034 -0
  359. multi_agent_brief/product/report_pack.py +130 -0
  360. multi_agent_brief/product/report_pack_aliases.py +68 -0
  361. multi_agent_brief/product/report_registry.py +101 -0
  362. multi_agent_brief/product/report_spec.py +181 -0
  363. multi_agent_brief/product/support_wording.py +525 -0
  364. multi_agent_brief/product/template_conformance.py +516 -0
  365. multi_agent_brief/product/template_projection.py +123 -0
  366. multi_agent_brief/product/template_registry.py +244 -0
  367. multi_agent_brief/product/template_render_plan.py +315 -0
  368. multi_agent_brief/product/template_renderer.py +224 -0
  369. multi_agent_brief/product/trajectory_regulation.py +441 -0
  370. multi_agent_brief/provenance/__init__.py +5 -0
  371. multi_agent_brief/provenance/builder.py +841 -0
  372. multi_agent_brief/provenance/contract.py +21 -0
  373. multi_agent_brief/provenance/io.py +144 -0
  374. multi_agent_brief/provenance/model.py +110 -0
  375. multi_agent_brief/provenance/references.py +43 -0
  376. multi_agent_brief/provenance/validator.py +135 -0
  377. multi_agent_brief/quality_gates/__init__.py +19 -0
  378. multi_agent_brief/quality_gates/contract.py +581 -0
  379. multi_agent_brief/quality_gates/state.py +2498 -0
  380. multi_agent_brief/repair/__init__.py +5 -0
  381. multi_agent_brief/repair/router.py +842 -0
  382. multi_agent_brief/runtime_assets.py +431 -0
  383. multi_agent_brief/sources/__init__.py +16 -0
  384. multi_agent_brief/sources/api_filings.py +236 -0
  385. multi_agent_brief/sources/api_news.py +174 -0
  386. multi_agent_brief/sources/base.py +142 -0
  387. multi_agent_brief/sources/cached_package.py +119 -0
  388. multi_agent_brief/sources/cli_provider.py +263 -0
  389. multi_agent_brief/sources/coverage.py +336 -0
  390. multi_agent_brief/sources/decider.py +756 -0
  391. multi_agent_brief/sources/doctor.py +373 -0
  392. multi_agent_brief/sources/evidence_pack.py +364 -0
  393. multi_agent_brief/sources/feishu_provider.py +326 -0
  394. multi_agent_brief/sources/filing_resolver.py +400 -0
  395. multi_agent_brief/sources/industry_packs.py +130 -0
  396. multi_agent_brief/sources/join.py +206 -0
  397. multi_agent_brief/sources/local_signal.py +81 -0
  398. multi_agent_brief/sources/local_signal_planner.py +636 -0
  399. multi_agent_brief/sources/manual.py +259 -0
  400. multi_agent_brief/sources/mcp_provider.py +309 -0
  401. multi_agent_brief/sources/mineru_provider.py +601 -0
  402. multi_agent_brief/sources/normalizer.py +95 -0
  403. multi_agent_brief/sources/opencli_provider.py +282 -0
  404. multi_agent_brief/sources/planner.py +178 -0
  405. multi_agent_brief/sources/registry.py +367 -0
  406. multi_agent_brief/sources/rss.py +190 -0
  407. multi_agent_brief/sources/search_backends/__init__.py +31 -0
  408. multi_agent_brief/sources/search_backends/base.py +51 -0
  409. multi_agent_brief/sources/search_backends/brave.py +192 -0
  410. multi_agent_brief/sources/search_backends/capabilities.py +94 -0
  411. multi_agent_brief/sources/search_backends/exa.py +180 -0
  412. multi_agent_brief/sources/search_backends/firecrawl.py +164 -0
  413. multi_agent_brief/sources/search_backends/serper.py +241 -0
  414. multi_agent_brief/sources/search_backends/tavily.py +124 -0
  415. multi_agent_brief/sources/sourcehub.py +505 -0
  416. multi_agent_brief/sources/web_search.py +267 -0
  417. multi_agent_brief/status.py +966 -0
  418. multi_agent_brief/tools/__init__.py +3 -0
  419. multi_agent_brief/tools/draft_cleanup.py +198 -0
  420. multi_agent_brief/workbuddy/__init__.py +1 -0
  421. multi_agent_brief/workbuddy/diagnose.py +569 -0
  422. multi_agent_brief/workbuddy/skill_pack.py +309 -0
  423. multi_agent_brief/workspace/__init__.py +1 -0
  424. multi_agent_brief/workspace/init_profile.py +45 -0
@@ -0,0 +1,647 @@
1
+ """sources and doctor — source discovery and health-check commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+
9
+ import yaml
10
+
11
+ from multi_agent_brief.sources.evidence_pack import (
12
+ SourceEvidencePackError,
13
+ materialize_source_evidence_pack,
14
+ )
15
+ from multi_agent_brief.sources.sourcehub import (
16
+ SourceHubError,
17
+ add_file_sources,
18
+ add_rss_feed,
19
+ add_web_search_handoff,
20
+ )
21
+ from multi_agent_brief.sources.decider import (
22
+ SourceCandidatesError,
23
+ load_source_discovery,
24
+ build_search_queries,
25
+ build_daily_news_search_tasks,
26
+ build_news_domain_preferences,
27
+ generate_source_candidates,
28
+ merge_candidates_to_sources,
29
+ )
30
+ from multi_agent_brief.sources.doctor import run_doctor, format_doctor_report
31
+ from multi_agent_brief.sources.registry import load_sources_config
32
+
33
+
34
+ def register_sources(subparsers: argparse._SubParsersAction) -> None:
35
+ """Register the sources subcommand group."""
36
+ sources_parser = subparsers.add_parser(
37
+ "sources", help="Source discovery and management."
38
+ )
39
+ sources_sub = sources_parser.add_subparsers(
40
+ dest="sources_action", required=True
41
+ )
42
+
43
+ decide_parser = sources_sub.add_parser(
44
+ "decide",
45
+ help="Resolve llm_decide profile into concrete source candidates.",
46
+ )
47
+ decide_parser.add_argument(
48
+ "--config", required=True, help="Path to config.yaml in the workspace."
49
+ )
50
+ decide_parser.add_argument(
51
+ "--search",
52
+ action="store_true",
53
+ help="Run web search to discover sources (requires search backend).",
54
+ )
55
+ decide_parser.add_argument(
56
+ "--daily-news-backfill",
57
+ action="store_true",
58
+ help=(
59
+ "Run one user-need-customized news search per day for the"
60
+ " recent backfill window."
61
+ ),
62
+ )
63
+ decide_parser.add_argument(
64
+ "--backfill-days",
65
+ type=int,
66
+ help="Number of past days for --daily-news-backfill. Default: 7.",
67
+ )
68
+ decide_parser.add_argument(
69
+ "--daily-max-results",
70
+ type=int,
71
+ help="Maximum search results per day for daily news backfill. Default: 20.",
72
+ )
73
+ decide_parser.add_argument(
74
+ "--merge",
75
+ action="store_true",
76
+ help="Merge approved source_candidates.yaml into sources.yaml.",
77
+ )
78
+ decide_parser.add_argument(
79
+ "--candidates",
80
+ help="Path to source_candidates.yaml (for --merge).",
81
+ )
82
+
83
+ materialize_parser = sources_sub.add_parser(
84
+ "materialize-pack",
85
+ help="Materialize explicit durable source records into input/sources/.",
86
+ )
87
+ materialize_parser.add_argument(
88
+ "--config", required=True, help="Path to config.yaml in the workspace."
89
+ )
90
+ materialize_parser.add_argument(
91
+ "--force",
92
+ action="store_true",
93
+ help="Replace existing generated source evidence records.",
94
+ )
95
+ materialize_parser.add_argument(
96
+ "--json",
97
+ action="store_true",
98
+ help="Emit machine-readable JSON.",
99
+ )
100
+
101
+ add_file_parser = sources_sub.add_parser(
102
+ "add-file",
103
+ help="Copy local text evidence files into the workspace and register them.",
104
+ )
105
+ _add_workspace_selector(add_file_parser)
106
+ add_file_parser.add_argument(
107
+ "paths",
108
+ nargs="+",
109
+ help="Local text source files or glob patterns (.md, .txt, .json).",
110
+ )
111
+ add_file_parser.add_argument(
112
+ "--name",
113
+ help="Reader-facing source name. Only valid with one file.",
114
+ )
115
+ add_file_parser.add_argument(
116
+ "--category",
117
+ default="other",
118
+ help="Reader-facing source category. Defaults to other.",
119
+ )
120
+ add_file_parser.add_argument(
121
+ "--language",
122
+ default="en",
123
+ help="Source language hint. Defaults to en.",
124
+ )
125
+ add_file_parser.add_argument(
126
+ "--json",
127
+ action="store_true",
128
+ help="Emit machine-readable JSON.",
129
+ )
130
+
131
+ add_rss_parser = sources_sub.add_parser(
132
+ "add-rss",
133
+ help="Register an RSS/Atom feed in sources.yaml.",
134
+ )
135
+ _add_workspace_selector(add_rss_parser)
136
+ add_rss_parser.add_argument("url", help="RSS/Atom feed URL.")
137
+ add_rss_parser.add_argument("--name", help="Feed display name.")
138
+ add_rss_parser.add_argument(
139
+ "--category",
140
+ default="news_media",
141
+ help="Reader-facing source category. Defaults to news_media.",
142
+ )
143
+ add_rss_parser.add_argument(
144
+ "--language",
145
+ default="en",
146
+ help="Feed language hint. Defaults to en.",
147
+ )
148
+ add_rss_parser.add_argument(
149
+ "--json",
150
+ action="store_true",
151
+ help="Emit machine-readable JSON.",
152
+ )
153
+
154
+ add_web_parser = sources_sub.add_parser(
155
+ "add-web-search",
156
+ help="Register a runtime web-search handoff task without executing search.",
157
+ )
158
+ _add_workspace_selector(add_web_parser)
159
+ add_web_parser.add_argument(
160
+ "--query",
161
+ required=True,
162
+ help="Search query for the runtime handoff.",
163
+ )
164
+ add_web_parser.add_argument(
165
+ "--domain",
166
+ action="append",
167
+ dest="domains",
168
+ default=[],
169
+ help="Preferred domain for the handoff task. Repeatable.",
170
+ )
171
+ add_web_parser.add_argument(
172
+ "--max-results",
173
+ type=int,
174
+ default=10,
175
+ help="Maximum results for the runtime handoff. Defaults to 10.",
176
+ )
177
+ add_web_parser.add_argument(
178
+ "--recency-days",
179
+ type=int,
180
+ help="Optional recency window for the runtime handoff.",
181
+ )
182
+ add_web_parser.add_argument(
183
+ "--json",
184
+ action="store_true",
185
+ help="Emit machine-readable JSON.",
186
+ )
187
+
188
+
189
+ def register_doctor(subparsers: argparse._SubParsersAction) -> None:
190
+ """Register the doctor subparser."""
191
+ doctor_parser = subparsers.add_parser(
192
+ "doctor", help="Check source configuration health."
193
+ )
194
+ doctor_parser.add_argument(
195
+ "--config", required=True, help="Path to config.yaml in the workspace."
196
+ )
197
+
198
+
199
+ def handle_sources(args: argparse.Namespace) -> int:
200
+ """Dispatch sources subcommands."""
201
+ if args.sources_action == "decide":
202
+ return _sources_decide(args)
203
+ if args.sources_action == "materialize-pack":
204
+ return _sources_materialize_pack(args)
205
+ if args.sources_action == "add-file":
206
+ return _sources_add_file(args)
207
+ if args.sources_action == "add-rss":
208
+ return _sources_add_rss(args)
209
+ if args.sources_action == "add-web-search":
210
+ return _sources_add_web_search(args)
211
+ return 1
212
+
213
+
214
+ def handle_doctor(args: argparse.Namespace) -> int:
215
+ """Run doctor health check."""
216
+ return _doctor(args)
217
+
218
+
219
+ def _doctor(args: argparse.Namespace) -> int:
220
+ results = run_doctor(config_path=args.config)
221
+ print(format_doctor_report(results))
222
+ errors = sum(1 for r in results if r.status == "ERROR")
223
+ return 1 if errors else 0
224
+
225
+
226
+ def _sources_decide(args: argparse.Namespace) -> int:
227
+ config_path = Path(args.config)
228
+ workspace = config_path.parent
229
+ sources_path = workspace / "sources.yaml"
230
+
231
+ if not sources_path.exists():
232
+ print(f"[error] sources.yaml not found: {sources_path}")
233
+ print(
234
+ "[hint] Run `briefloop init` first to create the"
235
+ " workspace."
236
+ )
237
+ return 1
238
+
239
+ discovery = load_source_discovery(sources_path)
240
+ if not discovery:
241
+ print(
242
+ "[error] No source_discovery section found in sources.yaml."
243
+ )
244
+ print(
245
+ "[hint] Re-init with --source-profile llm_decide to generate"
246
+ " discovery policy."
247
+ )
248
+ return 1
249
+
250
+ # --merge: merge candidates into sources.yaml
251
+ if args.merge:
252
+ candidates_path = (
253
+ Path(args.candidates)
254
+ if args.candidates
255
+ else workspace / "source_candidates.yaml"
256
+ )
257
+ if not candidates_path.exists():
258
+ print(
259
+ f"[error] source_candidates.yaml not found:"
260
+ f" {candidates_path}"
261
+ )
262
+ return 1
263
+ try:
264
+ result = merge_candidates_to_sources(sources_path, candidates_path)
265
+ except SourceCandidatesError as exc:
266
+ print(f"[error] {exc.error_code}: {exc}")
267
+ return 1
268
+ added_local = result.get("added_local", 0)
269
+ local_part = (
270
+ f" + {added_local} local signal tasks" if added_local else ""
271
+ )
272
+ print(
273
+ f"[sources] Merged {result['added_manual']} manual +"
274
+ f" {result['added_rss']} RSS{local_part} into sources.yaml"
275
+ )
276
+ return 0
277
+
278
+ ws_config = load_sources_config(sources_path) if sources_path.exists() else None
279
+ web_search_config = (
280
+ {**ws_config.web_search, "_workspace_dir": str(workspace)}
281
+ if ws_config
282
+ else {}
283
+ )
284
+ backfill_config = web_search_config.get("initial_news_backfill") or {}
285
+ use_daily_backfill = bool(args.daily_news_backfill) or bool(
286
+ backfill_config.get("enabled")
287
+ )
288
+ backfill_days = int(
289
+ args.backfill_days
290
+ or backfill_config.get("days")
291
+ or 7
292
+ )
293
+ daily_max_results = int(
294
+ args.daily_max_results
295
+ or backfill_config.get("daily_max_results")
296
+ or 20
297
+ )
298
+
299
+ # Default: generate source_candidates.yaml
300
+ search_tasks = (
301
+ build_daily_news_search_tasks(
302
+ discovery,
303
+ days=backfill_days,
304
+ daily_max_results=daily_max_results,
305
+ )
306
+ if use_daily_backfill
307
+ else _build_standard_search_tasks(discovery)
308
+ )
309
+ queries = [str(task.get("query", "")) for task in search_tasks if task.get("query")]
310
+ print(
311
+ f"[sources] Source discovery for:"
312
+ f" {discovery.get('company', 'N/A')}"
313
+ f" ({discovery.get('industry', 'N/A')})"
314
+ )
315
+ if use_daily_backfill:
316
+ print(
317
+ "[sources] Initial daily news backfill enabled:"
318
+ f" {backfill_days} days x {daily_max_results} results/day"
319
+ )
320
+ preferred_domains, excluded_domains = build_news_domain_preferences(discovery)
321
+ if preferred_domains:
322
+ print(
323
+ "[sources] Preferred news domains:"
324
+ f" {', '.join(preferred_domains)}"
325
+ )
326
+ if excluded_domains:
327
+ print(
328
+ "[sources] Excluded news domains:"
329
+ f" {', '.join(excluded_domains)}"
330
+ )
331
+ print(f"[sources] Generated {len(queries)} search queries:")
332
+ for i, q in enumerate(queries, 1):
333
+ print(f" {i}. {q}")
334
+
335
+ search_results = None
336
+ if args.search:
337
+ from multi_agent_brief.sources.web_search import (
338
+ WebSearchProvider,
339
+ backend_api_key_env,
340
+ temporary_workspace_api_key_env,
341
+ )
342
+
343
+ provider = WebSearchProvider()
344
+ validation_errors = provider.validate_config(web_search_config)
345
+ if web_search_config.get("enabled") is not True:
346
+ validation_errors.append(
347
+ "--search requires web_search.enabled: true; disabled web_search is a no-op."
348
+ )
349
+ if web_search_config.get("mode") != "external_api":
350
+ validation_errors.append(
351
+ "--search requires web_search.mode: external_api; runtime_tool/configure_later/disabled modes are not Python-searchable."
352
+ )
353
+ if validation_errors:
354
+ print(
355
+ "[error] --search requires a valid external_api web_search configuration."
356
+ )
357
+ for error in validation_errors:
358
+ print(f" {error}")
359
+ print(
360
+ " Supported backends: tavily, exa, brave, firecrawl,"
361
+ " serper."
362
+ )
363
+ print(
364
+ " Enable web_search in sources.yaml with mode:"
365
+ " external_api, a real backend, and API key,"
366
+ )
367
+ print(
368
+ " or run without --search to generate template"
369
+ " candidates."
370
+ )
371
+ return 1
372
+
373
+ # Actually execute searches via the configured backend
374
+ try:
375
+ backend = provider._get_backend(web_search_config)
376
+ except Exception as exc:
377
+ print(
378
+ f"[error] Failed to initialize search backend: {exc}"
379
+ )
380
+ return 1
381
+
382
+ with temporary_workspace_api_key_env(backend, web_search_config):
383
+ if not backend.is_available():
384
+ api_key_env = backend_api_key_env(
385
+ backend, web_search_config
386
+ )
387
+ key_hint = f" Set {api_key_env}." if api_key_env else ""
388
+ print(
389
+ f"[error] Search backend '{backend.name}' is configured"
390
+ f" but not available.{key_hint}"
391
+ )
392
+ return 1
393
+
394
+ print(
395
+ f"[sources] Executing {len(search_tasks)} search queries via"
396
+ f" backend: {backend.name}"
397
+ )
398
+ search_results = []
399
+ attempted_searches = 0
400
+ successful_searches = 0
401
+ total_result_count = 0
402
+ max_results = web_search_config.get("max_results", 10)
403
+ for task in search_tasks:
404
+ q = str(task.get("query", ""))
405
+ domains = task.get("domains") or None
406
+ task_max_results = int(task.get("max_results") or max_results)
407
+ attempted_searches += 1
408
+ try:
409
+ search_kwargs = {"domains": domains}
410
+ for key in ("topic", "vertical", "tbs"):
411
+ if task.get(key):
412
+ search_kwargs[key] = task[key]
413
+ if use_daily_backfill:
414
+ search_kwargs["days"] = backfill_days
415
+ elif web_search_config.get("recency_days"):
416
+ search_kwargs["days"] = web_search_config["recency_days"]
417
+ results = backend.search(
418
+ q,
419
+ max_results=task_max_results,
420
+ **search_kwargs,
421
+ )
422
+ search_results.append(
423
+ {
424
+ "query": q,
425
+ "metadata": {
426
+ key: value
427
+ for key, value in task.items()
428
+ if key not in ("query", "domains")
429
+ },
430
+ "results": [
431
+ {
432
+ "title": r.title,
433
+ "url": r.url,
434
+ "snippet": r.snippet,
435
+ "published_at": r.published_at,
436
+ "source_name": r.source_name,
437
+ }
438
+ for r in results
439
+ ],
440
+ }
441
+ )
442
+ successful_searches += 1
443
+ total_result_count += len(results)
444
+ print(f" [{len(results)} results] {q}")
445
+ except Exception as exc:
446
+ print(f" [error] Search failed for '{q}': {exc}")
447
+ # Continue with remaining queries, but do not write normal
448
+ # candidates if every backend request failed.
449
+ if attempted_searches and successful_searches == 0:
450
+ print(
451
+ "[error] All configured search queries failed; "
452
+ "source_candidates.yaml was not generated."
453
+ )
454
+ return 1
455
+ if attempted_searches and total_result_count == 0:
456
+ print(
457
+ "[error] All configured search queries returned zero results; "
458
+ "source_candidates.yaml was not generated."
459
+ )
460
+ return 1
461
+
462
+ candidates = generate_source_candidates(discovery, search_results)
463
+ candidates_path = workspace / "source_candidates.yaml"
464
+ try:
465
+ from multi_agent_brief.sources.decider import _save_yaml
466
+
467
+ _save_yaml(candidates_path, candidates)
468
+ except Exception as e:
469
+ print(f"[error] Failed to write source_candidates.yaml: {e}")
470
+ return 1
471
+
472
+ print(
473
+ f"[sources] Generated source_candidates.yaml at {candidates_path}"
474
+ )
475
+
476
+ # Generate collector_tasks.json if local signal tasks exist
477
+ from multi_agent_brief.sources.local_signal_planner import (
478
+ write_collector_tasks_json,
479
+ )
480
+
481
+ collector_path = (
482
+ workspace / "output" / "intermediate" / "collector_tasks.json"
483
+ )
484
+ collector_tasks = write_collector_tasks_json(discovery, collector_path)
485
+ if collector_tasks:
486
+ print(
487
+ f"[sources] Generated collector_tasks.json at"
488
+ f" {collector_path}"
489
+ )
490
+ print(
491
+ f"[sources] {len(collector_tasks['tasks'])} local signal"
492
+ " collection tasks ready"
493
+ )
494
+
495
+ print(
496
+ "[sources] Review and enable/disable sources, then run:"
497
+ )
498
+ print(
499
+ f" briefloop sources decide --config {args.config}"
500
+ " --merge"
501
+ )
502
+ return 0
503
+
504
+
505
+ def _sources_materialize_pack(args: argparse.Namespace) -> int:
506
+ try:
507
+ result = materialize_source_evidence_pack(
508
+ config_path=args.config,
509
+ force=bool(args.force),
510
+ )
511
+ except SourceEvidencePackError as exc:
512
+ if args.json:
513
+ print(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False, sort_keys=True))
514
+ else:
515
+ print(f"[error] {exc}")
516
+ return 1
517
+
518
+ if args.json:
519
+ print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True))
520
+ return 0
521
+
522
+ print(
523
+ "[sources] Materialized durable source evidence pack:"
524
+ f" {result['record_count']} records"
525
+ )
526
+ print(f"[sources] Manifest: {result['manifest_path']}")
527
+ if result.get("error_count"):
528
+ print(f"[sources] Provider errors recorded: {result['error_count']}")
529
+ print(
530
+ "[sources] Boundary: source evidence records are durable inputs, not"
531
+ " semantic support proof or delivery approval."
532
+ )
533
+ return 0
534
+
535
+
536
+ def _sources_add_file(args: argparse.Namespace) -> int:
537
+ try:
538
+ workspace = _workspace_from_sources_args(args)
539
+ result = add_file_sources(
540
+ workspace=workspace,
541
+ values=list(args.paths),
542
+ source_category=args.category,
543
+ language=args.language,
544
+ name=args.name,
545
+ )
546
+ except (OSError, SourceHubError, yaml.YAMLError) as exc:
547
+ return _print_sourcehub_error("sources.add-file", args, exc)
548
+ return _print_sourcehub_result("sources.add-file", args, result)
549
+
550
+
551
+ def _sources_add_rss(args: argparse.Namespace) -> int:
552
+ try:
553
+ workspace = _workspace_from_sources_args(args)
554
+ result = add_rss_feed(
555
+ workspace=workspace,
556
+ url=args.url,
557
+ name=args.name,
558
+ source_category=args.category,
559
+ language=args.language,
560
+ )
561
+ except (OSError, SourceHubError, yaml.YAMLError) as exc:
562
+ return _print_sourcehub_error("sources.add-rss", args, exc)
563
+ return _print_sourcehub_result("sources.add-rss", args, result)
564
+
565
+
566
+ def _sources_add_web_search(args: argparse.Namespace) -> int:
567
+ try:
568
+ workspace = _workspace_from_sources_args(args)
569
+ result = add_web_search_handoff(
570
+ workspace=workspace,
571
+ query=args.query,
572
+ domains=list(args.domains or []),
573
+ max_results=int(args.max_results),
574
+ recency_days=args.recency_days,
575
+ )
576
+ except (OSError, SourceHubError, yaml.YAMLError) as exc:
577
+ return _print_sourcehub_error("sources.add-web-search", args, exc)
578
+ return _print_sourcehub_result("sources.add-web-search", args, result)
579
+
580
+
581
+ def _add_workspace_selector(parser: argparse.ArgumentParser) -> None:
582
+ parser.add_argument(
583
+ "--workspace",
584
+ help="BriefLoop workspace path. Defaults to the current directory.",
585
+ )
586
+ parser.add_argument(
587
+ "--config",
588
+ help="Path to config.yaml. Overrides --workspace when provided.",
589
+ )
590
+
591
+
592
+ def _workspace_from_sources_args(args: argparse.Namespace) -> Path:
593
+ config = getattr(args, "config", None)
594
+ if config:
595
+ return Path(config).expanduser().resolve().parent
596
+ workspace = getattr(args, "workspace", None)
597
+ if workspace:
598
+ return Path(workspace).expanduser().resolve()
599
+ return Path.cwd().resolve()
600
+
601
+
602
+ def _print_sourcehub_error(label: str, args: argparse.Namespace, exc: Exception) -> int:
603
+ payload = {
604
+ "ok": False,
605
+ "action": label,
606
+ "error": str(exc),
607
+ "boundary": "sourcehub_lite_source_setup_only",
608
+ }
609
+ if getattr(args, "json", False):
610
+ print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True))
611
+ else:
612
+ print(f"[error] {exc}")
613
+ return 1
614
+
615
+
616
+ def _print_sourcehub_result(label: str, args: argparse.Namespace, result: dict) -> int:
617
+ if getattr(args, "json", False):
618
+ print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True))
619
+ return 0
620
+ print(f"[{label}] ok: {result.get('ok')}")
621
+ if label == "sources.add-file":
622
+ print(f"registered_files: {result.get('source_count')}")
623
+ for source in result.get("sources", []):
624
+ print(f"- {source.get('path')}")
625
+ print("boundary: source setup only; no collection, span extraction, or support judgment")
626
+ elif label == "sources.add-rss":
627
+ feed = result.get("feed") if isinstance(result.get("feed"), dict) else {}
628
+ print(f"feed: {feed.get('name')} <{feed.get('url')}>")
629
+ print("boundary: feed registration only; no fetch was run")
630
+ elif label == "sources.add-web-search":
631
+ task = result.get("task") if isinstance(result.get("task"), dict) else {}
632
+ print(f"query: {task.get('query')}")
633
+ print("boundary: runtime web-search handoff only; no Python search was run")
634
+ return 0
635
+
636
+
637
+ def _build_standard_search_tasks(discovery: dict) -> list[dict]:
638
+ preferred_domains, excluded_domains = build_news_domain_preferences(discovery)
639
+ tasks: list[dict] = []
640
+ for query in build_search_queries(discovery):
641
+ task = {"query": query, "domains": preferred_domains or None}
642
+ if preferred_domains:
643
+ task["preferred_domains"] = preferred_domains
644
+ if excluded_domains:
645
+ task["excluded_domains"] = excluded_domains
646
+ tasks.append(task)
647
+ return tasks
@@ -0,0 +1,8 @@
1
+ """Compatibility exports for runtime handoff domain helpers.
2
+
3
+ The implementation lives in :mod:`multi_agent_brief.orchestrator.handoff`.
4
+ This module remains for older internal imports and tests that still reference
5
+ ``multi_agent_brief.cli.start_commands``.
6
+ """
7
+
8
+ from multi_agent_brief.orchestrator.handoff import * # noqa: F401,F403