alphacouncil-agent 0.9.2 → 1.0.0
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.
- package/.claude/commands/alpha.md +22 -3
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +4 -4
- package/.grok/commands/alpha.md +22 -3
- package/.opencode/command/alpha.md +22 -3
- package/AGENTS.md +42 -4
- package/CHANGELOG.md +164 -0
- package/CLAUDE.md +59 -7
- package/README.ja.md +25 -11
- package/README.md +44 -12
- package/README.zh-CN.md +37 -12
- package/commands/alpha.md +22 -3
- package/data/authored/core-seats.mjs +808 -0
- package/data/authored/growth-seats.mjs +581 -0
- package/data/authored/index.mjs +114 -0
- package/data/authored/quant-seats.mjs +564 -0
- package/data/authored/value-seats.mjs +663 -0
- package/data/build-profile.v1.json +2 -1
- package/data/council-evaluation-protocol.v1.json +7 -4
- package/data/master-selector-method-locales.v1.mjs +296 -0
- package/data/persona-v3-ai-assisted-solo-profile.v1.json +2 -2
- package/data/persona-v3-build-specs.v1.mjs +60 -3
- package/docs/INSTALL.md +43 -6
- package/docs/personas.md +2 -1
- package/docs/plans/v1.0-roadmap.md +487 -0
- package/docs/releases/v0.9.3.md +86 -0
- package/docs/releases/v0.9.4.md +79 -0
- package/docs/releases/v0.9.5.md +96 -0
- package/docs/releases/v1.0.0.md +164 -0
- package/docs/report-contract.md +75 -2
- package/knowledge/ai-assisted-solo/experiments/runs/a.json +13 -13
- package/knowledge/ai-assisted-solo/experiments/runs/b.json +2 -2
- package/knowledge/ai-assisted-solo/experiments/runs/c.json +2 -2
- package/knowledge/ai-assisted-solo/experiments/runs/d13.json +126 -126
- package/knowledge/ai-assisted-solo/experiments/runs/d26.json +274 -254
- package/knowledge/ai-assisted-solo/experiments/runs/e-d13.json +129 -129
- package/knowledge/ai-assisted-solo/experiments/runs/e-d26.json +277 -257
- package/knowledge/ai-assisted-solo/experiments/runs/h_ai_reference.json +278 -258
- package/knowledge/ai-assisted-solo/experiments/simulation-input.json +7278 -556
- package/knowledge/ai-assisted-solo/experiments/simulation-manifest.json +22 -22
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_ackman/reviews/{change_value_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_ackman/reviews/{power_map.ai-review.json → owner_earnings_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_aschenbrenner/reviews/{compute_power_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_aschenbrenner/reviews/{timeline_reverse_valuation.ai-review.json → price_implied_cash_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_asness/reviews/{crowding_cost_stress.ai-review.json → nominal_earnings_yield_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_asness/reviews/{factor_decomposer.ai-review.json → real_earnings_yield_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/expected_return_over_long_bond.ai-review.json +277 -0
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/fundamental_expected_return.ai-review.json +277 -0
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_buffett/reviews/{incremental_returns.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_buffett/reviews/{owner_earnings_rebuilder.ai-review.json → owner_earnings_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_burry/reviews/{mispricing_carry.ai-review.json → net_current_asset_surplus_per_share.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_burry/reviews/{capital_structure_reader.ai-review.json → net_current_asset_value_per_share.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_cathie_wood/reviews/{cost_adoption_curve.ai-review.json → current_cash_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_cathie_wood/reviews/{capture_valuation_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_dalio/reviews/{debt_driver_stress.ai-review.json → policy_real_rate.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_dalio/reviews/{regime_classifier.ai-review.json → real_curve_slope.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_damodaran/reviews/{reverse_valuation.ai-review.json → implied_equity_risk_premium.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_damodaran/reviews/{story_dcf.ai-review.json → premium_versus_long_run_average.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_druckenmiller/reviews/{liquidity_revision_map.ai-review.json → liquidity_curve_impulse.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_druckenmiller/reviews/{inflection_payoff.ai-review.json → policy_real_rate.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{opportunity_cost.ai-review.json → five_year_opportunity_cost.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{user_value_evidence.ai-review.json → margin_retention.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_fisher/reviews/{research_productivity.ai-review.json → margin_retention.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_fisher/reviews/{scuttlebutt_graph.ai-review.json → reinvestment_spread.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_forensic_short/reviews/{borrow_catalyst.ai-review.json → cash_conversion_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_forensic_short/reviews/{accounting_rebuilder.ai-review.json → cash_cover_of_leverage.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_graham/reviews/{asset_floor.ai-review.json → defensive_hurdle_base.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_graham/reviews/{normalized_earnings.ai-review.json → half_normalised_earnings_yield.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_jhunjhunwala/reviews/{penetration_liquidity.ai-review.json → cash_quality_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_jhunjhunwala/reviews/{promoter_governance.ai-review.json → real_structural_growth.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_klarman/reviews/{recovery_waterfall.ai-review.json → downside_asset_coverage.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_klarman/reviews/{cash_catalyst_compare.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_li_lu/reviews/{promise_integrity_ledger.ai-review.json → cash_backed_decade_return.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_li_lu/reviews/{ten_year_return_bridge.ai-review.json → decade_return_over_long_bond.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_lynch/reviews/{story_numbers_check.ai-review.json → growth_justified_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_lynch/reviews/{category_classifier.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_marks/reviews/{cycle_temperature.ai-review.json → credit_cycle_position.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_marks/reviews/{implied_expectations.ai-review.json → equity_over_corporate_debt.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_munger/reviews/{failure_path_graph.ai-review.json → debt_service_cushion.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_munger/reviews/{incentive_map.ai-review.json → reported_earnings_cash_gap.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_natenberg/reviews/{surface_builder.ai-review.json → normalised_skew.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_natenberg/reviews/{greeks_payoff.ai-review.json → round_trip_cost.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_pabrai/reviews/{downside_floor.ai-review.json → downside_floor_coverage.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_pabrai/reviews/{discrete_payoff.ai-review.json → market_capitalisation.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{oos_cost_test.ai-review.json → absolute_session_move.ai-review.json} +42 -42
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{leakage_audit.ai-review.json → session_move_over_implied.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_sinclair/reviews/{realized_vol_forecast.ai-review.json → round_trip_cost.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_sinclair/reviews/{net_edge_sizer.ai-review.json → volatility_per_unit_of_width.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reversal_monitor.ai-review.json → credit_cycle_position.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reflexivity_graph.ai-review.json → financing_burden.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_taleb/reviews/{tail_friction.ai-review.json → debt_service_cushion.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_taleb/reviews/{payoff_ruin.ai-review.json → normalised_skew.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{edge_recalculator.ai-review.json → edge_per_unit_of_volatility.ai-review.json} +46 -46
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{fractional_kelly.ai-review.json → net_edge.ai-review.json} +49 -49
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/review-manifest.json +288 -270
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-adjudications/index.json +2 -2
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-extractions/index.json +2 -2
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-skeptic-reviews/index.json +2 -2
- package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-source-prereviews/index.json +6 -2
- package/knowledge/solo-test/masters/master_ackman/decision_policy.json +122 -11
- package/knowledge/solo-test/masters/master_ackman/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_ackman/manifest.json +27 -10
- package/knowledge/solo-test/masters/master_ackman/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_ackman/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_ackman/tools.json +56 -30
- package/knowledge/solo-test/masters/master_aschenbrenner/decision_policy.json +101 -10
- package/knowledge/solo-test/masters/master_aschenbrenner/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_aschenbrenner/manifest.json +26 -8
- package/knowledge/solo-test/masters/master_aschenbrenner/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_aschenbrenner/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_aschenbrenner/tools.json +56 -30
- package/knowledge/solo-test/masters/master_asness/decision_policy.json +89 -10
- package/knowledge/solo-test/masters/master_asness/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_asness/manifest.json +25 -8
- package/knowledge/solo-test/masters/master_asness/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_asness/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_asness/tools.json +56 -30
- package/knowledge/solo-test/masters/master_bogle/counterfactuals.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/decision_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/decision_policy.json +170 -0
- package/knowledge/solo-test/masters/master_bogle/doctrine.jsonl +14 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/calibration_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/experiments.json +5 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/golden_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/evaluation/pairwise_cases.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/failures.jsonl +0 -0
- package/knowledge/solo-test/masters/master_bogle/manifest.json +141 -0
- package/knowledge/solo-test/masters/master_bogle/memory_policy.json +5 -0
- package/knowledge/solo-test/masters/master_bogle/provisional-index.json +41 -0
- package/knowledge/solo-test/masters/master_bogle/research_policy.json +11 -0
- package/knowledge/solo-test/masters/master_bogle/sources.jsonl +1 -0
- package/knowledge/solo-test/masters/master_bogle/tools.json +120 -0
- package/knowledge/solo-test/masters/master_bogle/voice.en.md +7 -0
- package/knowledge/solo-test/masters/master_bogle/voice.zh.md +7 -0
- package/knowledge/solo-test/masters/master_buffett/decision_policy.json +102 -11
- package/knowledge/solo-test/masters/master_buffett/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_buffett/manifest.json +24 -8
- package/knowledge/solo-test/masters/master_buffett/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_buffett/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_buffett/tools.json +55 -29
- package/knowledge/solo-test/masters/master_burry/decision_policy.json +102 -12
- package/knowledge/solo-test/masters/master_burry/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_burry/manifest.json +26 -10
- package/knowledge/solo-test/masters/master_burry/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_burry/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_burry/tools.json +56 -30
- package/knowledge/solo-test/masters/master_cathie_wood/decision_policy.json +74 -12
- package/knowledge/solo-test/masters/master_cathie_wood/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_cathie_wood/manifest.json +26 -10
- package/knowledge/solo-test/masters/master_cathie_wood/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_cathie_wood/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_cathie_wood/tools.json +56 -30
- package/knowledge/solo-test/masters/master_dalio/decision_policy.json +123 -16
- package/knowledge/solo-test/masters/master_dalio/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_dalio/manifest.json +24 -8
- package/knowledge/solo-test/masters/master_dalio/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_dalio/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_dalio/tools.json +56 -30
- package/knowledge/solo-test/masters/master_damodaran/decision_policy.json +58 -10
- package/knowledge/solo-test/masters/master_damodaran/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_damodaran/manifest.json +23 -8
- package/knowledge/solo-test/masters/master_damodaran/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_damodaran/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_damodaran/tools.json +54 -28
- package/knowledge/solo-test/masters/master_druckenmiller/decision_policy.json +89 -10
- package/knowledge/solo-test/masters/master_druckenmiller/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_druckenmiller/manifest.json +25 -8
- package/knowledge/solo-test/masters/master_druckenmiller/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_druckenmiller/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_druckenmiller/tools.json +55 -29
- package/knowledge/solo-test/masters/master_duan_yongping/decision_policy.json +138 -13
- package/knowledge/solo-test/masters/master_duan_yongping/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_duan_yongping/manifest.json +31 -11
- package/knowledge/solo-test/masters/master_duan_yongping/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_duan_yongping/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_duan_yongping/tools.json +82 -30
- package/knowledge/solo-test/masters/master_fisher/decision_policy.json +102 -11
- package/knowledge/solo-test/masters/master_fisher/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_fisher/manifest.json +27 -10
- package/knowledge/solo-test/masters/master_fisher/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_fisher/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_fisher/tools.json +58 -32
- package/knowledge/solo-test/masters/master_forensic_short/decision_policy.json +93 -14
- package/knowledge/solo-test/masters/master_forensic_short/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_forensic_short/manifest.json +20 -8
- package/knowledge/solo-test/masters/master_forensic_short/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_forensic_short/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_forensic_short/tools.json +53 -27
- package/knowledge/solo-test/masters/master_graham/decision_policy.json +103 -12
- package/knowledge/solo-test/masters/master_graham/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_graham/manifest.json +26 -8
- package/knowledge/solo-test/masters/master_graham/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_graham/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_graham/tools.json +67 -28
- package/knowledge/solo-test/masters/master_jhunjhunwala/decision_policy.json +77 -12
- package/knowledge/solo-test/masters/master_jhunjhunwala/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_jhunjhunwala/manifest.json +25 -10
- package/knowledge/solo-test/masters/master_jhunjhunwala/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_jhunjhunwala/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_jhunjhunwala/tools.json +54 -28
- package/knowledge/solo-test/masters/master_klarman/decision_policy.json +105 -14
- package/knowledge/solo-test/masters/master_klarman/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_klarman/manifest.json +22 -6
- package/knowledge/solo-test/masters/master_klarman/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_klarman/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_klarman/tools.json +55 -29
- package/knowledge/solo-test/masters/master_li_lu/decision_policy.json +101 -10
- package/knowledge/solo-test/masters/master_li_lu/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_li_lu/manifest.json +27 -10
- package/knowledge/solo-test/masters/master_li_lu/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_li_lu/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_li_lu/tools.json +58 -32
- package/knowledge/solo-test/masters/master_lynch/decision_policy.json +127 -16
- package/knowledge/solo-test/masters/master_lynch/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_lynch/manifest.json +28 -10
- package/knowledge/solo-test/masters/master_lynch/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_lynch/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_lynch/tools.json +67 -28
- package/knowledge/solo-test/masters/master_marks/decision_policy.json +71 -9
- package/knowledge/solo-test/masters/master_marks/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_marks/manifest.json +25 -10
- package/knowledge/solo-test/masters/master_marks/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_marks/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_marks/tools.json +54 -28
- package/knowledge/solo-test/masters/master_munger/decision_policy.json +141 -16
- package/knowledge/solo-test/masters/master_munger/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_munger/manifest.json +24 -10
- package/knowledge/solo-test/masters/master_munger/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_munger/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_munger/tools.json +52 -26
- package/knowledge/solo-test/masters/master_natenberg/decision_policy.json +59 -11
- package/knowledge/solo-test/masters/master_natenberg/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_natenberg/manifest.json +23 -10
- package/knowledge/solo-test/masters/master_natenberg/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_natenberg/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_natenberg/tools.json +50 -24
- package/knowledge/solo-test/masters/master_pabrai/decision_policy.json +125 -14
- package/knowledge/solo-test/masters/master_pabrai/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_pabrai/manifest.json +22 -6
- package/knowledge/solo-test/masters/master_pabrai/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_pabrai/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_pabrai/tools.json +55 -29
- package/knowledge/solo-test/masters/master_simons/decision_policy.json +59 -11
- package/knowledge/solo-test/masters/master_simons/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_simons/manifest.json +22 -8
- package/knowledge/solo-test/masters/master_simons/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_simons/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_simons/tools.json +43 -30
- package/knowledge/solo-test/masters/master_sinclair/decision_policy.json +59 -11
- package/knowledge/solo-test/masters/master_sinclair/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_sinclair/manifest.json +22 -10
- package/knowledge/solo-test/masters/master_sinclair/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_sinclair/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_sinclair/tools.json +54 -28
- package/knowledge/solo-test/masters/master_soros/decision_policy.json +98 -10
- package/knowledge/solo-test/masters/master_soros/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_soros/manifest.json +24 -11
- package/knowledge/solo-test/masters/master_soros/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_soros/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_soros/tools.json +54 -28
- package/knowledge/solo-test/masters/master_taleb/decision_policy.json +124 -13
- package/knowledge/solo-test/masters/master_taleb/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_taleb/manifest.json +23 -9
- package/knowledge/solo-test/masters/master_taleb/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_taleb/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_taleb/tools.json +50 -24
- package/knowledge/solo-test/masters/master_thorp/decision_policy.json +61 -13
- package/knowledge/solo-test/masters/master_thorp/doctrine.jsonl +2 -2
- package/knowledge/solo-test/masters/master_thorp/manifest.json +25 -10
- package/knowledge/solo-test/masters/master_thorp/provisional-index.json +3 -3
- package/knowledge/solo-test/masters/master_thorp/sources.jsonl +1 -1
- package/knowledge/solo-test/masters/master_thorp/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/compilation-manifest.json +290 -264
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/price_implied_cash_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/nominal_earnings_yield_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/real_earnings_yield_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/components/tools.json +120 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/expected_return_over_long_bond.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/fundamental_expected_return.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/net_current_asset_surplus_per_share.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/net_current_asset_value_per_share.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/current_cash_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/real_curve_slope.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/implied_equity_risk_premium.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/{reverse_valuation.derived-proxy-evidence.json → premium_versus_long_run_average.derived-proxy-evidence.json} +29 -16
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_curve_impulse.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/components/tools.json +84 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/five_year_opportunity_cost.derived-proxy-evidence.json +102 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/components/tools.json +60 -34
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/reinvestment_spread.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/components/tools.json +55 -29
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/{accounting_rebuilder.derived-proxy-evidence.json → cash_conversion_gap.derived-proxy-evidence.json} +28 -15
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/cash_cover_of_leverage.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/components/tools.json +69 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/defensive_hurdle_base.derived-proxy-evidence.json +89 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/half_normalised_earnings_yield.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/cash_quality_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/real_structural_growth.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/downside_asset_coverage.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/components/tools.json +60 -34
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/cash_backed_decade_return.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/decade_return_over_long_bond.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/components/tools.json +69 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/growth_justified_capitalisation.derived-proxy-evidence.json +89 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/equity_over_corporate_debt.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/components/tools.json +54 -28
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/reported_earnings_cash_gap.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/components/tools.json +52 -26
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/provisional-derivations/{surface_builder.derived-proxy-evidence.json → normalised_skew.derived-proxy-evidence.json} +27 -14
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/components/tools.json +57 -31
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor_coverage.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/components/tools.json +45 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/{master_natenberg/provisional-derivations/greeks_payoff.derived-proxy-evidence.json → master_simons/provisional-derivations/absolute_session_move.derived-proxy-evidence.json} +17 -17
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/session_move_over_implied.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/volatility_per_unit_of_width.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/components/tools.json +56 -30
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/financing_burden.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/components/tools.json +52 -26
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/{tail_friction.derived-proxy-evidence.json → normalised_skew.derived-proxy-evidence.json} +27 -14
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/components/tools.json +58 -32
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_per_unit_of_volatility.derived-proxy-evidence.json +77 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/net_edge.derived-proxy-evidence.json +76 -0
- package/mcp/lib/breadth.mjs +122 -0
- package/mcp/lib/codex.mjs +3 -3
- package/mcp/lib/constants.mjs +76 -25
- package/mcp/lib/council-options.mjs +95 -40
- package/mcp/lib/council-selection.mjs +95 -9
- package/mcp/lib/damodaran.mjs +119 -0
- package/mcp/lib/fred.mjs +246 -0
- package/mcp/lib/fund-holdings-parsers.mjs +297 -0
- package/mcp/lib/fundamentals.mjs +801 -0
- package/mcp/lib/funds.mjs +750 -0
- package/mcp/lib/gates.mjs +149 -15
- package/mcp/lib/grounding.mjs +188 -36
- package/mcp/lib/index-aggregate.mjs +718 -0
- package/mcp/lib/insider-ownership.mjs +149 -0
- package/mcp/lib/instrument-facts.mjs +557 -0
- package/mcp/lib/instruments.mjs +191 -0
- package/mcp/lib/lang.mjs +109 -2
- package/mcp/lib/markdown.mjs +620 -138
- package/mcp/lib/master-catalog.mjs +32 -3
- package/mcp/lib/orchestrator.mjs +1121 -290
- package/mcp/lib/packets.mjs +227 -21
- package/mcp/lib/personas/engine.mjs +44 -31
- package/mcp/lib/personas-v3/compiler.mjs +2 -0
- package/mcp/lib/personas-v3/deterministic-executor.mjs +13 -2
- package/mcp/lib/personas-v3/ga-external-evidence.mjs +3 -2
- package/mcp/lib/personas-v3/ga-gate.mjs +2 -1
- package/mcp/lib/personas-v3/ga-package-evidence.mjs +5 -4
- package/mcp/lib/personas-v3/grounding-adapter.mjs +309 -2
- package/mcp/lib/personas-v3/loader.mjs +10 -7
- package/mcp/lib/personas-v3/production-root.mjs +5 -4
- package/mcp/lib/personas-v3/release-formula-evidence.mjs +14 -7
- package/mcp/lib/personas-v3/release-source-evidence.mjs +2 -1
- package/mcp/lib/personas-v3/releases.mjs +5 -4
- package/mcp/lib/personas-v3/runtime-release-integrity.mjs +6 -5
- package/mcp/lib/personas-v3/semantic-source-adjudication.mjs +2 -1
- package/mcp/lib/personas-v3/semantic-source-extraction.mjs +2 -1
- package/mcp/lib/personas-v3/semantic-source-skeptic-review.mjs +2 -1
- package/mcp/lib/personas-v3/source-acquisition.mjs +10 -3
- package/mcp/lib/personas-v3/source-review-operations.mjs +3 -2
- package/mcp/lib/personas-v3/staging.mjs +22 -3
- package/mcp/lib/prompts.mjs +119 -2
- package/mcp/lib/quotes.mjs +5 -0
- package/mcp/lib/rpc.mjs +101 -42
- package/mcp/lib/run-store.mjs +28 -3
- package/mcp/lib/sec.mjs +32 -0
- package/mcp/lib/tables.mjs +11 -0
- package/mcp/lib/voice.mjs +122 -0
- package/mcp/lib/xbrl-aggregates.mjs +153 -0
- package/mcp/lib/xbrl-series.mjs +339 -0
- package/mcp/lib/xlsx.mjs +155 -0
- package/package.json +5 -5
- package/personas/masters/masters-value/bogle.md +145 -0
- package/schemas/council-evaluation-protocol-v1.schema.json +2 -2
- package/schemas/persona-v3-ai-assisted-solo-profile-v1.schema.json +2 -2
- package/schemas/persona-v3-ai-machine-simulation-v1.schema.json +1 -1
- package/schemas/persona-v3-build-spec-v1.schema.json +3 -3
- package/schemas/persona-v3-release-formula-review-evidence-v1.schema.json +3 -3
- package/schemas/persona-v3-release-manifest-v1.schema.json +6 -6
- package/schemas/persona-v3-release-source-review-evidence-v1.schema.json +3 -3
- package/schemas/persona-v3-tool-graph-v1.schema.json +1 -1
- package/schemas/persona-v3.schema.json +37 -2
- package/schemas/source-review-batch-v1.schema.json +9 -9
- package/scripts/assemble-persona-v3-release.mjs +3 -2
- package/scripts/build-persona-v3-solo-test-packs.mjs +6 -8
- package/scripts/check-persona-v3-ga.mjs +1 -1
- package/scripts/check-persona-v3-production-candidates.mjs +1 -1
- package/scripts/compile-persona-v3-formulas.mjs +19 -18
- package/scripts/doctor.mjs +11 -9
- package/scripts/export-persona-source-review-batch.mjs +1 -1
- package/scripts/generate-persona-v3-drafts.mjs +2 -2
- package/scripts/lib/build-profile.mjs +24 -0
- package/scripts/lib/council-evaluation-protocol.mjs +7 -6
- package/scripts/lib/host-capabilities.mjs +2 -1
- package/scripts/lib/packaged-host-parity.mjs +63 -8
- package/scripts/lib/persona-v3-ai-assisted-solo-status.mjs +6 -4
- package/scripts/lib/persona-v3-ai-formula-review.mjs +6 -5
- package/scripts/lib/persona-v3-ai-machine-simulations.mjs +32 -4
- package/scripts/lib/persona-v3-build-specs.mjs +3 -2
- package/scripts/lib/persona-v3-draft-packs.mjs +2 -2
- package/scripts/lib/persona-v3-formula-pipeline.mjs +11 -10
- package/scripts/lib/persona-v3-production-candidates.mjs +10 -9
- package/scripts/lib/persona-v3-solo-formula-pipeline.mjs +182 -16
- package/scripts/lib/persona-v3-solo-test-packs.mjs +143 -16
- package/scripts/package-smoke.mjs +32 -12
- package/scripts/report-persona-corpus-gaps.mjs +4 -1
- package/scripts/review-persona-v3-solo-formulas.mjs +12 -8
- package/scripts/run-tests.mjs +23 -2
- package/scripts/scaffold-persona-v3-staging.mjs +1 -1
- package/skills/agent-skills-governance/SKILL.md +8 -2
- package/skills/alphacouncil-agent/SKILL.md +60 -9
- package/skills/public-equity-investing/SKILL.md +17 -0
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/change_value_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/power_map.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/compute_power_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/timeline_reverse_valuation.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/crowding_cost_stress.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/factor_decomposer.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/incremental_returns.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_rebuilder.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/capital_structure_reader.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/mispricing_carry.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/capture_valuation_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/cost_adoption_curve.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/debt_driver_stress.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/regime_classifier.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/story_dcf.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/inflection_payoff.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_revision_map.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/opportunity_cost.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/user_value_evidence.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/research_productivity.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/scuttlebutt_graph.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/borrow_catalyst.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/asset_floor.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/normalized_earnings.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/penetration_liquidity.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/promoter_governance.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/cash_catalyst_compare.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/recovery_waterfall.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/promise_integrity_ledger.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/ten_year_return_bridge.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/category_classifier.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/story_numbers_check.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/cycle_temperature.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/implied_expectations.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/failure_path_graph.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/incentive_map.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/discrete_payoff.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/leakage_audit.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/oos_cost_test.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/net_edge_sizer.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/realized_vol_forecast.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reflexivity_graph.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reversal_monitor.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/payoff_ruin.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_recalculator.derived-proxy-evidence.json +0 -64
- package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/fractional_kelly.derived-proxy-evidence.json +0 -64
package/mcp/lib/orchestrator.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { COUNCIL_MODES, DEBATE_ROLES, DEFAULT_TASKS, LIMITS, OUTPUT_MODES, QUICK_TASKS } from "./constants.mjs";
|
|
4
4
|
import { invalidParams } from "./errors.mjs";
|
|
5
5
|
import { readJson, writeJson } from "./fsutil.mjs";
|
|
6
6
|
import { registry } from "./personas/registry.mjs";
|
|
7
|
-
import { isChineseLanguage, resolveLanguage } from "./lang.mjs";
|
|
7
|
+
import { assertReaderLanguage, isChineseLanguage, localized, resolveLanguage } from "./lang.mjs";
|
|
8
8
|
import { cleanLog } from "./text.mjs";
|
|
9
|
-
import { completenessStatus, verificationStatus } from "./gates.mjs";
|
|
9
|
+
import { completenessStatus, masterSeatIncomplete, verificationStatus } from "./gates.mjs";
|
|
10
10
|
import { agentState, appendEvent, artifactPaths, existingDebate, runPath, runId, safeSymbol, saveRun, taskState, today, updateAgent, updateTask, writeSourceManifest, writeStatus } from "./run-store.mjs";
|
|
11
11
|
import { writeAllAgentsMarkdown, writeAnalystMarkdownFiles, writeArtifactIndex, writeFinalArtifacts } from "./markdown.mjs";
|
|
12
|
-
import { debateFromCodex, debateQnaGate, dryDebate, dryPacket, extractJson, firstFailedDebateResult, managerFallback, mergeDebateRounds, normalizeDebate, normalizeMasterOpinion, normalizePacket, rawRecordText } from "./packets.mjs";
|
|
12
|
+
import { debateFailurePacket, debateFromCodex, debateQnaGate, dryDebate, dryPacket, extractJson, firstFailedDebateResult, managerFallback, mergeDebateRounds, normalizeDebate, normalizeMasterOpinion, normalizeMasterVoice, normalizePacket, rawRecordText } from "./packets.mjs";
|
|
13
13
|
import { mapLimit, runCodex } from "./codex.mjs";
|
|
14
|
-
import { debatePrompt, masterPrompt, selectedMasters, taskPrompt } from "./prompts.mjs";
|
|
14
|
+
import { debatePrompt, masterPrompt, masterVoicePrompt, selectedMasters, taskPrompt } from "./prompts.mjs";
|
|
15
15
|
import { resolveSeatWeights } from "./weights.mjs";
|
|
16
16
|
import { completedMasterOpinion, declinedMasterOpinion, planMasterSeats, reconcileMasterOpinion } from "./personas/engine.mjs";
|
|
17
17
|
import { gatherGrounding } from "./grounding.mjs";
|
|
@@ -28,40 +28,67 @@ function plannedTasks(args = {}) {
|
|
|
28
28
|
return DEFAULT_TASKS;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
return { council_mode: "full", debate_format: "three_round_cross_exam", time_budget_ms: null, deadline_at: null };
|
|
34
|
-
}
|
|
31
|
+
function councilTiming(args, startedAt) {
|
|
32
|
+
const mode = councilMode(args);
|
|
35
33
|
const requested = Number(args.total_timeout_ms);
|
|
34
|
+
const hardMaximum = mode === "quick" ? LIMITS.QUICK_HARD_MAX_MS : LIMITS.FULL_HARD_MAX_MS;
|
|
35
|
+
const defaultBudget = mode === "quick" ? LIMITS.QUICK_TOTAL_MS : LIMITS.FULL_TOTAL_MS;
|
|
36
36
|
const timeBudgetMs = Number.isFinite(requested) && requested > 0
|
|
37
|
-
? Math.min(requested,
|
|
38
|
-
:
|
|
37
|
+
? Math.min(requested, hardMaximum)
|
|
38
|
+
: defaultBudget;
|
|
39
39
|
const deadlineAt = args.queued_run?.deadline_at
|
|
40
40
|
|| new Date(Date.parse(startedAt) + timeBudgetMs).toISOString();
|
|
41
41
|
return {
|
|
42
|
-
council_mode:
|
|
43
|
-
debate_format: "single_round_parallel",
|
|
42
|
+
council_mode: mode,
|
|
43
|
+
debate_format: mode === "quick" ? "single_round_parallel" : "three_round_cross_exam_parallel_per_round",
|
|
44
44
|
time_budget_ms: timeBudgetMs,
|
|
45
45
|
deadline_at: deadlineAt,
|
|
46
|
+
deadline_enforced: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function visibleCouncilTiming(args) {
|
|
51
|
+
const mode = councilMode(args);
|
|
52
|
+
return {
|
|
53
|
+
council_mode: mode,
|
|
54
|
+
debate_format: mode === "quick" ? "single_round_parallel" : "host_managed_visible_debate",
|
|
55
|
+
time_budget_ms: null,
|
|
56
|
+
deadline_at: null,
|
|
57
|
+
deadline_enforced: false,
|
|
46
58
|
};
|
|
47
59
|
}
|
|
48
60
|
|
|
49
|
-
function
|
|
50
|
-
if (
|
|
61
|
+
function remainingCouncilBudget(run, capMs) {
|
|
62
|
+
if (!run.deadline_at) return capMs;
|
|
63
|
+
const configuredReserve = run.council_mode === "quick"
|
|
64
|
+
? LIMITS.QUICK_FINALIZE_RESERVE_MS
|
|
65
|
+
: LIMITS.FULL_FINALIZE_RESERVE_MS;
|
|
51
66
|
const reserve = Math.min(
|
|
52
|
-
|
|
53
|
-
Math.max(100, Math.floor(Number(run.time_budget_ms || LIMITS.
|
|
67
|
+
configuredReserve,
|
|
68
|
+
Math.max(100, Math.floor(Number(run.time_budget_ms || LIMITS.FULL_TOTAL_MS) * 0.1)),
|
|
54
69
|
);
|
|
55
|
-
const
|
|
70
|
+
const killGrace = councilKillGrace(run);
|
|
71
|
+
// runCodex may need one SIGKILL grace after its timeout. Budget that grace before
|
|
72
|
+
// launching a child so the queue-to-persistence deadline remains the outer boundary.
|
|
73
|
+
const usable = Date.parse(run.deadline_at) - Date.now() - reserve - killGrace;
|
|
56
74
|
return Math.max(0, Math.min(capMs, usable));
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
function
|
|
77
|
+
function councilKillGrace(run) {
|
|
78
|
+
const total = Math.max(1_000, Number(run?.time_budget_ms || LIMITS.FULL_TOTAL_MS));
|
|
79
|
+
// A fixed five-second grace would consume tiny operator-supplied budgets before the
|
|
80
|
+
// first worker. Production budgets retain the full grace; small budgets scale it down.
|
|
81
|
+
// The exact same value is passed to runCodex, so forced settlement remains inside the
|
|
82
|
+
// queue-to-persistence deadline rather than becoming an unaccounted overrun.
|
|
83
|
+
return Math.min(LIMITS.SIGKILL_GRACE_MS, Math.max(50, Math.floor(total * 0.02)));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function deadlineResult(run) {
|
|
60
87
|
return {
|
|
61
88
|
ok: false,
|
|
62
89
|
code: null,
|
|
63
90
|
text: "",
|
|
64
|
-
stderr: "
|
|
91
|
+
stderr: `${run?.council_mode || "council"} global deadline exhausted`,
|
|
65
92
|
stdout: "",
|
|
66
93
|
timedOut: true,
|
|
67
94
|
deadline_exhausted: true,
|
|
@@ -169,7 +196,10 @@ export function visibleRun(args) {
|
|
|
169
196
|
const dir = runPath(id);
|
|
170
197
|
mkdirSync(dir, { recursive: true });
|
|
171
198
|
const startedAt = new Date().toISOString();
|
|
172
|
-
|
|
199
|
+
// External host threads are outside this MCP process, so the plugin cannot stop them or
|
|
200
|
+
// make a truthful queue-to-persistence SLA claim. Only analyze_symbol gets the enforced
|
|
201
|
+
// thirty-minute full-council deadline.
|
|
202
|
+
const timing = visibleCouncilTiming(args);
|
|
173
203
|
const run = {
|
|
174
204
|
run_id: id,
|
|
175
205
|
symbol,
|
|
@@ -188,6 +218,18 @@ export function visibleRun(args) {
|
|
|
188
218
|
tasks,
|
|
189
219
|
task_status: Object.fromEntries(tasks.map((task) => [task, { task, status: "pending" }])),
|
|
190
220
|
agent_status: Object.fromEntries(DEBATE_ROLES.map((role) => [role, { role, status: "pending" }])),
|
|
221
|
+
// Visible hosts call record_visible_decision once per role and round. Keep those
|
|
222
|
+
// packets separate: overwriting bull_researcher.json three times destroys the audit
|
|
223
|
+
// chain and makes a one-round shortcut indistinguishable from a full cross-exam.
|
|
224
|
+
visible_debate: {
|
|
225
|
+
contract: "role_round_audit_v1",
|
|
226
|
+
rounds_expected: councilMode(args) === "quick" ? 1 : 3,
|
|
227
|
+
rounds: { bull_researcher: {}, bear_researcher: {} },
|
|
228
|
+
qna_gate: {
|
|
229
|
+
status: councilMode(args) === "quick" ? "not_run" : "pending",
|
|
230
|
+
errors: [],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
191
233
|
packets: [],
|
|
192
234
|
// The receipt resolves the user's exact choice once. This frozen list, not a dynamic
|
|
193
235
|
// roster lookup, is the run truth and every seat is part of the completeness gate.
|
|
@@ -220,9 +262,19 @@ export function visibleRun(args) {
|
|
|
220
262
|
export function visibleAgentSpecs(run, userPrompt = "") {
|
|
221
263
|
const evidence_agents = run.tasks.map((task) => ({
|
|
222
264
|
role: task,
|
|
223
|
-
title:
|
|
265
|
+
title: localized(run.language, {
|
|
266
|
+
en: `AlphaCouncil Agent ${run.symbol} ${task} evidence subagent`,
|
|
267
|
+
zh: `AlphaCouncil Agent ${run.symbol} ${task} 证据子代理`,
|
|
268
|
+
ja: `AlphaCouncil Agent ${run.symbol} ${task} 証拠サブエージェント`,
|
|
269
|
+
ko: `AlphaCouncil Agent ${run.symbol} ${task} 증거 하위 에이전트`,
|
|
270
|
+
}),
|
|
224
271
|
prompt: taskPrompt(task, run.symbol, run.as_of, userPrompt, run.language, run.grounding),
|
|
225
|
-
output_contract:
|
|
272
|
+
output_contract: localized(run.language, {
|
|
273
|
+
en: `Return one JSON evidence packet with reader-facing fields in ${run.language}.`,
|
|
274
|
+
zh: "只返回一个 JSON evidence packet。",
|
|
275
|
+
ja: "読者向けフィールドを日本語にした JSON evidence packet を1つだけ返してください。",
|
|
276
|
+
ko: "독자용 필드를 한국어로 작성한 JSON evidence packet 하나만 반환하십시오.",
|
|
277
|
+
}),
|
|
226
278
|
}));
|
|
227
279
|
const debate_agents = DEBATE_ROLES.map((role) => ({
|
|
228
280
|
role,
|
|
@@ -230,15 +282,17 @@ export function visibleAgentSpecs(run, userPrompt = "") {
|
|
|
230
282
|
prompt_template: [
|
|
231
283
|
debatePrompt(role, run),
|
|
232
284
|
"",
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
285
|
+
localized(run.language, {
|
|
286
|
+
en: "The main thread must paste the completed Evidence JSON before running this visible agent.", zh: "主线程必须先粘贴已完成的 Evidence JSON,再运行这个可见代理。", ja: "メインスレッドは、この可視エージェントを実行する前に完成済みの Evidence JSON を貼り付ける必要があります。", ko: "메인 스레드는 이 표시형 에이전트를 실행하기 전에 완료된 Evidence JSON을 붙여 넣어야 합니다.",
|
|
287
|
+
}),
|
|
288
|
+
role === "bear_researcher" ? localized(run.language, { en: "The main thread must also paste Bull argument JSON.", zh: "主线程还必须粘贴 Bull argument JSON。", ja: "メインスレッドは Bull argument JSON も貼り付ける必要があります。", ko: "메인 스레드는 Bull argument JSON도 붙여 넣어야 합니다." }) : "",
|
|
289
|
+
role === "portfolio_manager" ? localized(run.language, { en: "The main thread must also paste Bull and Bear argument JSON.", zh: "主线程还必须粘贴 Bull 和 Bear argument JSON。", ja: "メインスレッドは Bull と Bear の argument JSON も貼り付ける必要があります。", ko: "메인 스레드는 Bull 및 Bear argument JSON도 붙여 넣어야 합니다." }) : "",
|
|
236
290
|
].filter(Boolean).join("\n"),
|
|
237
|
-
output_contract:
|
|
291
|
+
output_contract: localized(run.language, { en: `Return one JSON debate packet with reader-facing fields in ${run.language}.`, zh: "只返回一个 JSON debate packet。", ja: "読者向けフィールドを日本語にした JSON debate packet を1つだけ返してください。", ko: "독자용 필드를 한국어로 작성한 JSON debate packet 하나만 반환하십시오." }),
|
|
238
292
|
}));
|
|
239
|
-
// The deterministic pass
|
|
240
|
-
//
|
|
241
|
-
//
|
|
293
|
+
// The deterministic pass freezes every physical v3 stance before language generation.
|
|
294
|
+
// Visible v3 workers below may explain that frozen result after evidence completes, but
|
|
295
|
+
// cannot turn an out_of_scope method into a directional vote.
|
|
242
296
|
const plan = planMasterSeats(run, selectedMasters(run));
|
|
243
297
|
run.master_decisions = plan.decisions;
|
|
244
298
|
run.fact_pack_hash = plan.shared_fact_pack_hash;
|
|
@@ -253,35 +307,58 @@ export function visibleAgentSpecs(run, userPrompt = "") {
|
|
|
253
307
|
"",
|
|
254
308
|
decision ? deterministicVerdictBlock(decision, zh) : "",
|
|
255
309
|
"",
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
: "
|
|
310
|
+
localized(run.language, {
|
|
311
|
+
en: "The main thread must paste the completed Evidence JSON first. Masters run after the evidence stage and before the debate.",
|
|
312
|
+
zh: "主线程必须先粘贴已完成的 Evidence JSON,再运行这个大师议席;大师在证据之后、辩论之前运行。",
|
|
313
|
+
ja: "メインスレッドは先に完成済みの Evidence JSON を貼り付ける必要があります。メソッド席は証拠段階の後、討論の前に実行します。",
|
|
314
|
+
ko: "메인 스레드는 먼저 완료된 Evidence JSON을 붙여 넣어야 합니다. 방법론 좌석은 증거 단계 이후, 토론 이전에 실행합니다.",
|
|
315
|
+
}),
|
|
259
316
|
].filter(Boolean).join("\n"),
|
|
260
|
-
output_contract:
|
|
261
|
-
|
|
262
|
-
:
|
|
317
|
+
output_contract: localized(run.language, {
|
|
318
|
+
en: `Return one JSON master opinion with reader-facing fields in ${run.language}.`,
|
|
319
|
+
zh: "只返回一个 JSON master opinion。",
|
|
320
|
+
ja: "読者向けフィールドを日本語にした JSON master opinion を1つだけ返してください。",
|
|
321
|
+
ko: "독자용 필드를 한국어로 작성한 JSON master opinion 하나만 반환하십시오.",
|
|
322
|
+
}),
|
|
263
323
|
}));
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
// ever spawned for a method that cannot look.
|
|
324
|
+
// Persist the deterministic fallback before the visible worker runs. It remains auditable
|
|
325
|
+
// if the host fails, but a full visible PM now waits for the returned explanation worker.
|
|
267
326
|
if (plan.declined.length) {
|
|
268
327
|
const byId = new Map((run.master_opinions || []).map((o) => [o.master, o]));
|
|
269
328
|
for (const item of plan.declined) {
|
|
270
329
|
if (!byId.has(item.id)) {
|
|
330
|
+
const fallback = declinedMasterOpinion(run, item);
|
|
271
331
|
byId.set(item.id, attachMasterRuntimeProvenance(
|
|
272
332
|
run,
|
|
273
333
|
item.id,
|
|
274
|
-
|
|
334
|
+
{
|
|
335
|
+
...fallback,
|
|
336
|
+
voice_statement: fallback.voice_statement || fallback.summary || fallback.verdict,
|
|
337
|
+
voice_status: fallback.voice_status || "deterministic_fallback",
|
|
338
|
+
statement_origin: fallback.statement_origin || "deterministic_scope_fallback",
|
|
339
|
+
},
|
|
275
340
|
item.engine,
|
|
276
341
|
));
|
|
277
342
|
}
|
|
343
|
+
// A declined seat already carries a readable deterministic scope statement and a frozen
|
|
344
|
+
// out_of_scope stance no worker may change, so the explanation worker could not alter
|
|
345
|
+
// the record -- it only cost one sequential model turn per seat on a host with no
|
|
346
|
+
// fan-out. On a full bench that was 26 extra turns, which roughly doubled visible
|
|
347
|
+
// wall-clock. Seats that actually reached a decision still get their worker below.
|
|
348
|
+
const requiresVisibleVoice = false;
|
|
349
|
+
const alreadyVoiced = run.master_status?.[item.id]?.status === "completed"
|
|
350
|
+
&& byId.get(item.id)?.voice_status === "completed";
|
|
351
|
+
const completed = alreadyVoiced || !requiresVisibleVoice;
|
|
278
352
|
run.master_status[item.id] = {
|
|
279
353
|
...(run.master_status[item.id] || {}),
|
|
280
354
|
master: item.id,
|
|
281
|
-
status: "completed",
|
|
355
|
+
status: completed ? "completed" : "waiting",
|
|
282
356
|
engine: item.engine || "v2_method_model",
|
|
283
357
|
deterministic_decline: true,
|
|
284
|
-
|
|
358
|
+
voice_required: requiresVisibleVoice,
|
|
359
|
+
completed_at: completed
|
|
360
|
+
? (run.master_status[item.id]?.completed_at || new Date().toISOString())
|
|
361
|
+
: null,
|
|
285
362
|
};
|
|
286
363
|
}
|
|
287
364
|
run.master_opinions = selectedMasters(run).map((id) => byId.get(id)).filter(Boolean);
|
|
@@ -297,13 +374,16 @@ export function visibleAgentSpecs(run, userPrompt = "") {
|
|
|
297
374
|
item.engine,
|
|
298
375
|
));
|
|
299
376
|
}
|
|
377
|
+
const alreadyVoiced = run.master_status?.[item.id]?.status === "completed"
|
|
378
|
+
&& byId.get(item.id)?.voice_status === "completed";
|
|
300
379
|
run.master_status[item.id] = {
|
|
301
380
|
...(run.master_status[item.id] || {}),
|
|
302
381
|
master: item.id,
|
|
303
|
-
status: "completed",
|
|
382
|
+
status: alreadyVoiced ? "completed" : "waiting",
|
|
304
383
|
engine: item.engine,
|
|
305
384
|
deterministic_execution: true,
|
|
306
|
-
|
|
385
|
+
voice_required: true,
|
|
386
|
+
completed_at: alreadyVoiced ? run.master_status[item.id].completed_at : null,
|
|
307
387
|
};
|
|
308
388
|
}
|
|
309
389
|
run.master_opinions = selectedMasters(run).map((id) => byId.get(id)).filter(Boolean);
|
|
@@ -318,10 +398,43 @@ export function visibleAgentSpecs(run, userPrompt = "") {
|
|
|
318
398
|
updated_at: new Date().toISOString(),
|
|
319
399
|
};
|
|
320
400
|
}
|
|
401
|
+
const frozenById = new Map((run.master_opinions || []).map((opinion) => [opinion.master, opinion]));
|
|
402
|
+
const v3VoiceAgents = plan.completed
|
|
403
|
+
.filter((item) => item.engine === "v3_method_runtime")
|
|
404
|
+
.filter((item) => run.master_status?.[item.id]?.status !== "completed")
|
|
405
|
+
.map((item) => {
|
|
406
|
+
const frozenOpinion = frozenById.get(item.id);
|
|
407
|
+
return {
|
|
408
|
+
role: item.id,
|
|
409
|
+
engine: "v3_method_runtime",
|
|
410
|
+
worker_kind: "visible_method_voice",
|
|
411
|
+
frozen_stance: frozenOpinion?.stance || "out_of_scope",
|
|
412
|
+
title: `AlphaCouncil Agent ${run.symbol} ${item.id} method-seat explanation`,
|
|
413
|
+
prompt_template: [
|
|
414
|
+
masterVoicePrompt(item.id, run, frozenOpinion),
|
|
415
|
+
localized(run.language, {
|
|
416
|
+
en: "The main thread MUST append the completed Evidence JSON before launching this visible worker. Use it only to explain or challenge analyst interpretation; never change the frozen stance or add a fact.",
|
|
417
|
+
zh: "主线程必须在启动这个可见方法席前附上已完成的 Evidence JSON。它只能用于解释或质疑分析师解读;不得改变冻结立场,也不得新增事实。",
|
|
418
|
+
ja: "メインスレッドは、この可視メソッド席を起動する前に完成済み Evidence JSON を追加してください。分析担当の解釈を説明・検討する目的だけに使い、凍結済みスタンスの変更や事実追加は禁止です。",
|
|
419
|
+
ko: "메인 스레드는 이 표시형 방법론 좌석을 시작하기 전에 완료된 Evidence JSON을 추가해야 합니다. 분석가 해석을 설명·검토하는 데만 사용하고 동결된 입장을 바꾸거나 사실을 추가하지 마십시오.",
|
|
420
|
+
}),
|
|
421
|
+
].join("\n\n"),
|
|
422
|
+
output_contract: localized(run.language, {
|
|
423
|
+
en: "Return one JSON method voice: master, acknowledged_stance, statement, key_findings, disagreements, what_would_change_my_mind, source_ids, confidence. acknowledged_stance must equal frozen_stance.",
|
|
424
|
+
zh: "只返回一个 JSON 方法席陈词:master、acknowledged_stance、statement、key_findings、disagreements、what_would_change_my_mind、source_ids、confidence;acknowledged_stance 必须等于冻结立场。",
|
|
425
|
+
ja: "master、acknowledged_stance、statement、key_findings、disagreements、what_would_change_my_mind、source_ids、confidence を持つ JSON を1つだけ返し、acknowledged_stance は凍結済みスタンスと一致させてください。",
|
|
426
|
+
ko: "master, acknowledged_stance, statement, key_findings, disagreements, what_would_change_my_mind, source_ids, confidence를 가진 JSON 하나만 반환하고 acknowledged_stance는 동결된 입장과 같아야 합니다.",
|
|
427
|
+
}),
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
master_agents.push(...v3VoiceAgents);
|
|
431
|
+
const orderedMasterAgents = selectedMasters(run)
|
|
432
|
+
.map((id) => master_agents.find((agent) => agent.role === id))
|
|
433
|
+
.filter(Boolean);
|
|
321
434
|
saveRun(run);
|
|
322
435
|
return {
|
|
323
436
|
evidence_agents,
|
|
324
|
-
master_agents,
|
|
437
|
+
master_agents: orderedMasterAgents,
|
|
325
438
|
debate_agents,
|
|
326
439
|
// Recorded, not hidden: a reader must be able to tell a method that judged from a method
|
|
327
440
|
// that could not look, and neither from a seat that was never offered.
|
|
@@ -395,18 +508,90 @@ export function recordMasterOpinion(args) {
|
|
|
395
508
|
if (!allowed.includes(args.master)) {
|
|
396
509
|
throw invalidParams(`master ${args.master} was not selected for this run. Selected: ${allowed.join(", ") || "none"}`);
|
|
397
510
|
}
|
|
511
|
+
const missingEvidence = (run.tasks || []).filter((task) => taskState(run, task).status !== "completed");
|
|
512
|
+
if (missingEvidence.length) {
|
|
513
|
+
throw invalidParams("record_master_opinion rejected: every planned evidence packet must complete first.", {
|
|
514
|
+
reason: "VISIBLE_MASTER_EVIDENCE_INCOMPLETE",
|
|
515
|
+
run_id: run.run_id,
|
|
516
|
+
missing_evidence: missingEvidence,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
398
519
|
const dir = runPath(run.run_id);
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
args.master
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
520
|
+
const frozenOpinion = (run.master_opinions || []).find((item) => item.master === args.master);
|
|
521
|
+
const v3Voice = frozenOpinion?.engine === "v3_method_runtime"
|
|
522
|
+
|| run.master_runtime_provenance?.[args.master]?.engine === "v3_method_runtime";
|
|
523
|
+
let opinion;
|
|
524
|
+
let overridden = false;
|
|
525
|
+
if (v3Voice) {
|
|
526
|
+
if (!frozenOpinion) throw invalidParams(`v3 master ${args.master} has no frozen deterministic opinion.`);
|
|
527
|
+
if (args.packet?.master !== args.master || args.packet?.acknowledged_stance !== frozenOpinion.stance) {
|
|
528
|
+
throw invalidParams("visible v3 method voice must acknowledge the exact frozen master and stance.", {
|
|
529
|
+
reason: "VISIBLE_MASTER_FROZEN_STANCE_MISMATCH",
|
|
530
|
+
run_id: run.run_id,
|
|
531
|
+
master: args.master,
|
|
532
|
+
expected_stance: frozenOpinion.stance,
|
|
533
|
+
supplied_master: args.packet?.master || null,
|
|
534
|
+
supplied_stance: args.packet?.acknowledged_stance || null,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
const voice = normalizeMasterVoice(
|
|
538
|
+
args.packet,
|
|
539
|
+
args.master,
|
|
540
|
+
run,
|
|
541
|
+
frozenOpinion,
|
|
542
|
+
rawRecordText(args.packet),
|
|
543
|
+
);
|
|
544
|
+
assertVisibleReaderLanguage([
|
|
545
|
+
voice.statement,
|
|
546
|
+
...(voice.key_findings || []),
|
|
547
|
+
...(voice.disagreements || []),
|
|
548
|
+
...(voice.what_would_change_my_mind || []),
|
|
549
|
+
].filter(Boolean).join("\n"), run, `visible master voice ${args.master}`);
|
|
550
|
+
opinion = attachMasterRuntimeProvenance(run, args.master, {
|
|
551
|
+
...frozenOpinion,
|
|
552
|
+
deterministic_summary: frozenOpinion.deterministic_summary || frozenOpinion.summary,
|
|
553
|
+
summary: voice.statement,
|
|
554
|
+
voice_statement: voice.statement,
|
|
555
|
+
voice_status: "completed",
|
|
556
|
+
voice_language: run.language,
|
|
557
|
+
statement_origin: "visible_method_voice_worker",
|
|
558
|
+
key_findings: voice.key_findings.length ? voice.key_findings : frozenOpinion.key_findings,
|
|
559
|
+
disagreements: voice.disagreements,
|
|
560
|
+
what_would_change_my_mind: voice.what_would_change_my_mind.length
|
|
561
|
+
? voice.what_would_change_my_mind
|
|
562
|
+
: frozenOpinion.what_would_change_my_mind,
|
|
563
|
+
source_ids: [...new Set([...(frozenOpinion.source_ids || []), ...voice.source_ids])],
|
|
564
|
+
confidence: voice.confidence,
|
|
565
|
+
thread_id: args.thread_id,
|
|
566
|
+
dedicated_worker: {
|
|
567
|
+
status: "completed",
|
|
568
|
+
language: run.language,
|
|
569
|
+
execution_mode: "visible_host_thread",
|
|
570
|
+
thread_id: args.thread_id,
|
|
571
|
+
},
|
|
572
|
+
}, "v3_method_runtime");
|
|
573
|
+
} else {
|
|
574
|
+
const normalized = normalizeMasterOpinion(
|
|
575
|
+
{ ...(args.packet || {}), thread_id: args.thread_id },
|
|
576
|
+
args.master,
|
|
577
|
+
run,
|
|
578
|
+
rawRecordText(args.packet),
|
|
579
|
+
);
|
|
580
|
+
assertVisibleReaderLanguage(masterReaderText(normalized), run, `visible master ${args.master}`);
|
|
581
|
+
// A narrated stance that disagrees with the arithmetic does not get to win quietly. The
|
|
582
|
+
// deterministic verdict stands and the disagreement is preserved on the record.
|
|
583
|
+
const reconciled = reconcileMasterOpinion(run, args.master, normalized);
|
|
584
|
+
opinion = attachMasterRuntimeProvenance(run, args.master, {
|
|
585
|
+
...reconciled.opinion,
|
|
586
|
+
voice_statement: reconciled.opinion.voice_statement
|
|
587
|
+
|| reconciled.opinion.summary
|
|
588
|
+
|| reconciled.opinion.verdict,
|
|
589
|
+
voice_status: "completed",
|
|
590
|
+
voice_language: run.language,
|
|
591
|
+
statement_origin: "visible_legacy_method_worker",
|
|
592
|
+
}, reconciled.engine);
|
|
593
|
+
overridden = reconciled.overridden;
|
|
594
|
+
}
|
|
410
595
|
const byId = new Map((run.master_opinions || []).map((item) => [item.master, item]));
|
|
411
596
|
byId.set(args.master, opinion);
|
|
412
597
|
run.master_opinions = allowed.map((id) => byId.get(id)).filter(Boolean);
|
|
@@ -453,6 +638,7 @@ export function recordVisiblePacket(args) {
|
|
|
453
638
|
thread_title: args.thread_title,
|
|
454
639
|
execution_mode: "visible_host_threads",
|
|
455
640
|
}, task, run.symbol, run.as_of, rawRecordText(args.packet));
|
|
641
|
+
assertVisibleReaderLanguage(visibleEvidenceReaderText(packet), run, `visible evidence ${task}`);
|
|
456
642
|
const byTask = new Map(run.packets.map((item) => [item.task, item]));
|
|
457
643
|
byTask.set(task, packet);
|
|
458
644
|
run.packets = run.tasks.map((item) => byTask.get(item)).filter(Boolean);
|
|
@@ -472,70 +658,380 @@ export function recordVisiblePacket(args) {
|
|
|
472
658
|
return run;
|
|
473
659
|
}
|
|
474
660
|
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
661
|
+
function visibleDebateState(run) {
|
|
662
|
+
const expected = run.council_mode === "quick" ? 1 : 3;
|
|
663
|
+
run.visible_debate = run.visible_debate && typeof run.visible_debate === "object"
|
|
664
|
+
? run.visible_debate
|
|
665
|
+
: {};
|
|
666
|
+
run.visible_debate.contract = "role_round_audit_v1";
|
|
667
|
+
run.visible_debate.rounds_expected = expected;
|
|
668
|
+
run.visible_debate.rounds = run.visible_debate.rounds && typeof run.visible_debate.rounds === "object"
|
|
669
|
+
? run.visible_debate.rounds
|
|
670
|
+
: {};
|
|
671
|
+
for (const role of ["bull_researcher", "bear_researcher"]) {
|
|
672
|
+
run.visible_debate.rounds[role] = run.visible_debate.rounds[role]
|
|
673
|
+
&& typeof run.visible_debate.rounds[role] === "object"
|
|
674
|
+
? run.visible_debate.rounds[role]
|
|
675
|
+
: {};
|
|
676
|
+
}
|
|
677
|
+
run.visible_debate.qna_gate = run.visible_debate.qna_gate && typeof run.visible_debate.qna_gate === "object"
|
|
678
|
+
? run.visible_debate.qna_gate
|
|
679
|
+
: { status: expected === 1 ? "not_run" : "pending", errors: [] };
|
|
680
|
+
return run.visible_debate;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function visibleRoundEntry(state, role, round) {
|
|
684
|
+
return state?.rounds?.[role]?.[String(round)] || null;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function visibleRoundPacket(state, role, round) {
|
|
688
|
+
return visibleRoundEntry(state, role, round)?.packet || null;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function visibleRoundNumbers(state, role) {
|
|
692
|
+
return Object.keys(state?.rounds?.[role] || {})
|
|
693
|
+
.map(Number)
|
|
694
|
+
.filter(Number.isInteger)
|
|
695
|
+
.sort((a, b) => a - b);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function visibleDecisionContentHash(packet) {
|
|
699
|
+
const content = JSON.parse(JSON.stringify(packet || {}));
|
|
700
|
+
delete content.raw_text;
|
|
701
|
+
delete content.thread_id;
|
|
702
|
+
delete content.thread_title;
|
|
703
|
+
delete content.execution_mode;
|
|
704
|
+
return sha256(content);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function roundGateArgs(state, role, round, candidate) {
|
|
708
|
+
const get = (candidateRole, candidateRound) => (
|
|
709
|
+
candidateRole === role && candidateRound === round
|
|
710
|
+
? candidate
|
|
711
|
+
: visibleRoundPacket(state, candidateRole, candidateRound)
|
|
712
|
+
);
|
|
713
|
+
return {
|
|
714
|
+
bullR2: get("bull_researcher", 2),
|
|
715
|
+
bearR2: get("bear_researcher", 2),
|
|
716
|
+
bullR3: get("bull_researcher", 3),
|
|
717
|
+
bearR3: get("bear_researcher", 3),
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function rejectVisibleDecision(run, reason, message, data = {}) {
|
|
722
|
+
throw invalidParams(message, { reason, run_id: run.run_id, ...data });
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function assertVisibleRoundOrder(run, state, role, round) {
|
|
726
|
+
if (round === 1) return;
|
|
727
|
+
const previous = round - 1;
|
|
728
|
+
const missing = ["bull_researcher", "bear_researcher"]
|
|
729
|
+
.filter((side) => !visibleRoundEntry(state, side, previous))
|
|
730
|
+
.map((side) => `${side}:${previous}`);
|
|
731
|
+
if (missing.length) {
|
|
732
|
+
rejectVisibleDecision(
|
|
733
|
+
run,
|
|
734
|
+
"VISIBLE_DEBATE_ROUND_OUT_OF_ORDER",
|
|
735
|
+
`Cannot record ${role} round ${round} before both round ${previous} packets are recorded.`,
|
|
736
|
+
{ role, round, missing_prerequisite_rounds: missing },
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function assertVisibleRoundQna(run, state, role, round, packet) {
|
|
742
|
+
if (run.council_mode === "quick" || round === 1) return;
|
|
743
|
+
const gate = debateQnaGate(roundGateArgs(state, role, round, packet));
|
|
744
|
+
const ownErrors = gate.errors.filter((error) => error.startsWith(`${role} round ${round}`));
|
|
745
|
+
if (ownErrors.length) {
|
|
746
|
+
rejectVisibleDecision(
|
|
747
|
+
run,
|
|
748
|
+
"VISIBLE_DEBATE_QNA_INVALID",
|
|
749
|
+
`Rejected ${role} round ${round}: ${ownErrors.join("; ")}`,
|
|
750
|
+
{ role, round, qna_gate: { status: "failed", errors: ownErrors } },
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
const other = role === "bull_researcher" ? "bear_researcher" : "bull_researcher";
|
|
754
|
+
if (round === 3 && visibleRoundEntry(state, other, 3) && gate.status !== "passed") {
|
|
755
|
+
rejectVisibleDecision(
|
|
756
|
+
run,
|
|
757
|
+
"VISIBLE_DEBATE_QNA_INVALID",
|
|
758
|
+
`Rejected ${role} round 3 because the completed cross-exam failed the exact Q&A gate.`,
|
|
759
|
+
{ role, round, qna_gate: gate },
|
|
760
|
+
);
|
|
479
761
|
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function visibleRoundAgentPatch(run, state, role) {
|
|
765
|
+
const dir = runPath(run.run_id);
|
|
766
|
+
const rounds = visibleRoundNumbers(state, role);
|
|
767
|
+
const latest = rounds.length ? visibleRoundEntry(state, role, rounds.at(-1)) : null;
|
|
768
|
+
return {
|
|
769
|
+
rounds_completed: rounds,
|
|
770
|
+
last_completed_round: rounds.at(-1) || null,
|
|
771
|
+
round_status: rounds.length ? "completed" : "pending",
|
|
772
|
+
qna_gate: state.qna_gate.status,
|
|
773
|
+
thread_id: latest?.thread_id,
|
|
774
|
+
thread_title: latest?.thread_title,
|
|
775
|
+
output: join(dir, `${role}.json`),
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function recordVisibleDebateRound(run, args) {
|
|
480
780
|
const role = args.role;
|
|
481
|
-
|
|
781
|
+
const missingEvidence = (run.tasks || []).filter((task) => taskState(run, task).status !== "completed");
|
|
782
|
+
const missingMasters = selectedMasters(run).filter((master) => masterSeatIncomplete(run, master));
|
|
783
|
+
if (missingEvidence.length || missingMasters.length) {
|
|
784
|
+
rejectVisibleDecision(
|
|
785
|
+
run,
|
|
786
|
+
"VISIBLE_DEBATE_PREREQUISITES_INCOMPLETE",
|
|
787
|
+
"Bull/Bear debate rejected: complete every evidence packet and returned method-seat worker first.",
|
|
788
|
+
{ missing_evidence: missingEvidence, missing_masters: missingMasters },
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
const expected = run.council_mode === "quick" ? [1] : [1, 2, 3];
|
|
792
|
+
if (!Number.isInteger(args.round) || !expected.includes(args.round)) {
|
|
793
|
+
rejectVisibleDecision(
|
|
794
|
+
run,
|
|
795
|
+
"VISIBLE_DEBATE_ROUND_REQUIRED",
|
|
796
|
+
`${role} requires an explicit round=${expected.join("|")}.`,
|
|
797
|
+
{ role, supplied_round: args.round ?? null, allowed_rounds: expected },
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
const round = args.round;
|
|
801
|
+
const dir = runPath(run.run_id);
|
|
802
|
+
const state = visibleDebateState(run);
|
|
803
|
+
const packet = {
|
|
804
|
+
...normalizeDebate({
|
|
805
|
+
...(args.packet || {}),
|
|
806
|
+
thread_id: args.thread_id,
|
|
807
|
+
thread_title: args.thread_title,
|
|
808
|
+
execution_mode: "visible_host_threads",
|
|
809
|
+
}, role, run, rawRecordText(args.packet)),
|
|
810
|
+
round,
|
|
811
|
+
// A caller records exactly one round. Nested supplied rounds would create a second,
|
|
812
|
+
// unaudited history inside the packet and are therefore discarded.
|
|
813
|
+
debate_rounds: [],
|
|
814
|
+
};
|
|
815
|
+
assertVisibleReaderLanguage(debateReaderText(packet), run, `visible debate ${role} round ${round}`);
|
|
816
|
+
const contentHash = visibleDecisionContentHash(packet);
|
|
817
|
+
const existing = visibleRoundEntry(state, role, round);
|
|
818
|
+
if (existing) {
|
|
819
|
+
if (existing.content_hash !== contentHash) {
|
|
820
|
+
rejectVisibleDecision(
|
|
821
|
+
run,
|
|
822
|
+
"VISIBLE_DEBATE_ROUND_CONFLICT",
|
|
823
|
+
`Conflicting replay for ${role} round ${round}.`,
|
|
824
|
+
{ role, round, existing_content_hash: existing.content_hash, submitted_content_hash: contentHash },
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
return { run, decision: existing.packet, idempotent_replay: true };
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
assertVisibleRoundOrder(run, state, role, round);
|
|
831
|
+
assertVisibleRoundQna(run, state, role, round, packet);
|
|
832
|
+
|
|
833
|
+
const completedAt = new Date().toISOString();
|
|
834
|
+
const roundFile = join(dir, `${role}.round-${round}.json`);
|
|
835
|
+
state.rounds[role][String(round)] = {
|
|
836
|
+
round,
|
|
837
|
+
role,
|
|
838
|
+
content_hash: contentHash,
|
|
839
|
+
packet,
|
|
840
|
+
thread_id: args.thread_id,
|
|
841
|
+
thread_title: args.thread_title,
|
|
842
|
+
output: roundFile,
|
|
843
|
+
completed_at: completedAt,
|
|
844
|
+
};
|
|
845
|
+
writeJson(roundFile, packet);
|
|
846
|
+
const merged = mergeDebateRounds(visibleRoundNumbers(state, role)
|
|
847
|
+
.map((item) => visibleRoundPacket(state, role, item)));
|
|
848
|
+
writeJson(join(dir, `${role}.json`), merged);
|
|
849
|
+
|
|
850
|
+
const quick = run.council_mode === "quick";
|
|
851
|
+
const hasBothFinalRounds = ["bull_researcher", "bear_researcher"]
|
|
852
|
+
.every((side) => visibleRoundEntry(state, side, quick ? 1 : 3));
|
|
853
|
+
if (quick) {
|
|
854
|
+
state.qna_gate = { status: "not_run", errors: [], checked_at: completedAt };
|
|
855
|
+
updateAgent(run, role, "completed", {
|
|
856
|
+
...visibleRoundAgentPatch(run, state, role),
|
|
857
|
+
completed_at: completedAt,
|
|
858
|
+
});
|
|
859
|
+
} else if (hasBothFinalRounds) {
|
|
860
|
+
const qnaGate = debateQnaGate(roundGateArgs(state, role, round, packet));
|
|
861
|
+
// The candidate was validated before persistence. This guard protects future state
|
|
862
|
+
// migrations or a manually corrupted run file from manufacturing a passed audit.
|
|
863
|
+
if (qnaGate.status !== "passed") {
|
|
864
|
+
delete state.rounds[role][String(round)];
|
|
865
|
+
rejectVisibleDecision(
|
|
866
|
+
run,
|
|
867
|
+
"VISIBLE_DEBATE_QNA_INVALID",
|
|
868
|
+
"The completed visible cross-exam failed the exact Q&A gate.",
|
|
869
|
+
{ role, round, qna_gate: qnaGate },
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
state.qna_gate = { ...qnaGate, checked_at: completedAt };
|
|
873
|
+
for (const side of ["bull_researcher", "bear_researcher"]) {
|
|
874
|
+
updateAgent(run, side, "completed", {
|
|
875
|
+
...visibleRoundAgentPatch(run, state, side),
|
|
876
|
+
completed_at: completedAt,
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
appendEvent(run, "debate_qna_gate", state.qna_gate);
|
|
880
|
+
} else {
|
|
881
|
+
updateAgent(run, role, "waiting", visibleRoundAgentPatch(run, state, role));
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
run.status = "running";
|
|
885
|
+
run.phase = "visible_debate";
|
|
886
|
+
run.completed_at = null;
|
|
887
|
+
saveRun(run);
|
|
888
|
+
appendEvent(run, "visible_debate_round_recorded", {
|
|
889
|
+
role,
|
|
890
|
+
round,
|
|
891
|
+
content_hash: contentHash,
|
|
892
|
+
output: roundFile,
|
|
893
|
+
qna_gate: state.qna_gate.status,
|
|
894
|
+
});
|
|
895
|
+
writeAnalystMarkdownFiles(run, existingDebate(dir));
|
|
896
|
+
writeArtifactIndex(run, existingDebate(dir));
|
|
897
|
+
writeAllAgentsMarkdown(run, existingDebate(dir));
|
|
898
|
+
return { run, decision: packet, idempotent_replay: false };
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function visiblePmPrerequisites(run, state) {
|
|
902
|
+
const missingEvidence = (run.tasks || [])
|
|
903
|
+
.filter((task) => taskState(run, task).status !== "completed");
|
|
904
|
+
const missingMasters = selectedMasters(run).filter((master) => masterSeatIncomplete(run, master));
|
|
905
|
+
const expected = run.council_mode === "quick" ? [1] : [1, 2, 3];
|
|
906
|
+
const missingRounds = ["bull_researcher", "bear_researcher"].flatMap((role) => (
|
|
907
|
+
expected.filter((round) => !visibleRoundEntry(state, role, round)).map((round) => `${role}:${round}`)
|
|
908
|
+
));
|
|
909
|
+
const missingSides = ["bull_researcher", "bear_researcher"]
|
|
910
|
+
.filter((role) => agentState(run, role).status !== "completed");
|
|
911
|
+
const qnaRequired = run.council_mode !== "quick";
|
|
912
|
+
return {
|
|
913
|
+
missing_evidence: missingEvidence,
|
|
914
|
+
missing_masters: missingMasters,
|
|
915
|
+
missing_debate_rounds: missingRounds,
|
|
916
|
+
missing_debate_sides: missingSides,
|
|
917
|
+
qna_gate: state.qna_gate,
|
|
918
|
+
passed: missingEvidence.length === 0
|
|
919
|
+
&& missingMasters.length === 0
|
|
920
|
+
&& missingRounds.length === 0
|
|
921
|
+
&& missingSides.length === 0
|
|
922
|
+
&& (!qnaRequired || state.qna_gate.status === "passed"),
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function recordVisiblePortfolioManager(run, args) {
|
|
927
|
+
if (args.round !== undefined && args.round !== null) {
|
|
928
|
+
rejectVisibleDecision(
|
|
929
|
+
run,
|
|
930
|
+
"VISIBLE_PM_ROUND_FORBIDDEN",
|
|
931
|
+
"portfolio_manager is the final decision and does not accept a debate round.",
|
|
932
|
+
{ supplied_round: args.round },
|
|
933
|
+
);
|
|
934
|
+
}
|
|
482
935
|
const dir = runPath(run.run_id);
|
|
936
|
+
const state = visibleDebateState(run);
|
|
483
937
|
const packet = normalizeDebate({
|
|
484
938
|
...(args.packet || {}),
|
|
485
939
|
thread_id: args.thread_id,
|
|
486
940
|
thread_title: args.thread_title,
|
|
487
941
|
execution_mode: "visible_host_threads",
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
942
|
+
}, "portfolio_manager", run, rawRecordText(args.packet));
|
|
943
|
+
assertVisibleReaderLanguage(debateReaderText(packet), run, "visible portfolio_manager decision");
|
|
944
|
+
const contentHash = visibleDecisionContentHash(packet);
|
|
945
|
+
if (state.portfolio_manager) {
|
|
946
|
+
if (state.portfolio_manager.content_hash !== contentHash) {
|
|
947
|
+
rejectVisibleDecision(run, "VISIBLE_PM_CONFLICT", "Conflicting portfolio_manager replay.", {
|
|
948
|
+
existing_content_hash: state.portfolio_manager.content_hash,
|
|
949
|
+
submitted_content_hash: contentHash,
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
const handoffPath = join(dir, "user_response.md");
|
|
953
|
+
const reportPath = join(dir, "final_report.md");
|
|
954
|
+
const qualityPath = join(dir, "report_quality.json");
|
|
955
|
+
return {
|
|
956
|
+
run,
|
|
957
|
+
decision: state.portfolio_manager.packet,
|
|
958
|
+
idempotent_replay: true,
|
|
959
|
+
user_response_markdown: existsSync(handoffPath) ? readFileSync(handoffPath, "utf8") : "",
|
|
960
|
+
final_report_markdown: existsSync(reportPath) ? readFileSync(reportPath, "utf8") : "",
|
|
961
|
+
report_quality: existsSync(qualityPath) ? readJson(qualityPath) : null,
|
|
962
|
+
artifacts: artifactPaths(run),
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const prerequisites = visiblePmPrerequisites(run, state);
|
|
967
|
+
if (!prerequisites.passed) {
|
|
968
|
+
rejectVisibleDecision(
|
|
969
|
+
run,
|
|
970
|
+
"VISIBLE_PM_PREREQUISITES_INCOMPLETE",
|
|
971
|
+
"portfolio_manager rejected: complete evidence, every selected master, all required bull/bear rounds, and the exact Q&A gate are mandatory first.",
|
|
972
|
+
prerequisites,
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const completedAt = new Date().toISOString();
|
|
977
|
+
const file = join(dir, "manager_synthesis.json");
|
|
978
|
+
state.portfolio_manager = {
|
|
979
|
+
content_hash: contentHash,
|
|
980
|
+
packet,
|
|
496
981
|
thread_id: args.thread_id,
|
|
497
982
|
thread_title: args.thread_title,
|
|
498
|
-
output:
|
|
983
|
+
output: file,
|
|
984
|
+
completed_at: completedAt,
|
|
985
|
+
};
|
|
986
|
+
writeJson(file, packet);
|
|
987
|
+
writeJson(join(dir, "decision.json"), packet);
|
|
988
|
+
updateAgent(run, "portfolio_manager", "completed", {
|
|
989
|
+
completed_at: completedAt,
|
|
990
|
+
thread_id: args.thread_id,
|
|
991
|
+
thread_title: args.thread_title,
|
|
992
|
+
output: file,
|
|
993
|
+
debate_qna_gate: state.qna_gate.status,
|
|
499
994
|
});
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
missing_masters: completeness.missing_masters,
|
|
512
|
-
});
|
|
513
|
-
} else if (gate.verification === "needs_verification") {
|
|
514
|
-
run.status = "needs_verification";
|
|
515
|
-
run.phase = "needs_verification";
|
|
516
|
-
appendEvent(run, "needs_verification", { missing: gate.missing_claim_source_ids.length });
|
|
517
|
-
} else {
|
|
518
|
-
run.status = "complete";
|
|
519
|
-
run.phase = "complete";
|
|
520
|
-
}
|
|
521
|
-
run.completed_at = new Date().toISOString();
|
|
522
|
-
} else {
|
|
523
|
-
run.status = "running";
|
|
524
|
-
run.phase = "visible_debate";
|
|
995
|
+
|
|
996
|
+
const gate = verificationStatus(run);
|
|
997
|
+
const completeness = completenessStatus(run);
|
|
998
|
+
if (completeness.completeness === "incomplete") {
|
|
999
|
+
// Defensive only: visiblePmPrerequisites rejects all known incomplete states before
|
|
1000
|
+
// this point. Never create a final report if a future completeness field disagrees.
|
|
1001
|
+
rejectVisibleDecision(run, "VISIBLE_PM_PREREQUISITES_INCOMPLETE", "portfolio_manager completeness gate failed.", {
|
|
1002
|
+
missing_evidence: completeness.missing_evidence,
|
|
1003
|
+
missing_debate: completeness.missing_debate,
|
|
1004
|
+
missing_masters: completeness.missing_masters,
|
|
1005
|
+
});
|
|
525
1006
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
writeJson(join(dir, "evidence.json"), run);
|
|
531
|
-
if (run.status === "complete") appendEvent(run, "run_complete", { decision: packet.rating, winner: packet.winner });
|
|
1007
|
+
if (gate.verification === "needs_verification") {
|
|
1008
|
+
run.status = "needs_verification";
|
|
1009
|
+
run.phase = "needs_verification";
|
|
1010
|
+
appendEvent(run, "needs_verification", { missing: gate.missing_claim_source_ids.length });
|
|
532
1011
|
} else {
|
|
533
|
-
|
|
534
|
-
|
|
1012
|
+
run.status = "complete";
|
|
1013
|
+
run.phase = "complete";
|
|
535
1014
|
}
|
|
1015
|
+
run.completed_at = completedAt;
|
|
1016
|
+
saveRun(run);
|
|
1017
|
+
const finalArtifacts = writeFinalArtifacts(run, existingDebate(dir));
|
|
1018
|
+
saveRun(run);
|
|
1019
|
+
if (run.status === "complete") appendEvent(run, "run_complete", { decision: packet.rating, winner: packet.winner });
|
|
536
1020
|
writeStatus(run);
|
|
537
1021
|
writeAllAgentsMarkdown(run, existingDebate(dir));
|
|
538
|
-
return { run, decision: packet, ...finalArtifacts };
|
|
1022
|
+
return { run, decision: packet, idempotent_replay: false, ...finalArtifacts };
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
export function recordVisibleDecision(args) {
|
|
1026
|
+
const run = readJson(join(runPath(args.run_id), "evidence.json"));
|
|
1027
|
+
if (run.execution_mode !== "visible_host_threads") {
|
|
1028
|
+
throw invalidParams("record_visible_decision requires a run created by plan_visible_run.");
|
|
1029
|
+
}
|
|
1030
|
+
const role = args.role;
|
|
1031
|
+
if (!DEBATE_ROLES.includes(role)) throw invalidParams(`Unknown decision role: ${role}`);
|
|
1032
|
+
return role === "portfolio_manager"
|
|
1033
|
+
? recordVisiblePortfolioManager(run, args)
|
|
1034
|
+
: recordVisibleDebateRound(run, args);
|
|
539
1035
|
}
|
|
540
1036
|
|
|
541
1037
|
export function isDryRun(args = {}) {
|
|
@@ -550,30 +1046,51 @@ export function isDryRun(args = {}) {
|
|
|
550
1046
|
* but is not a sourced claim and must never enter evidence.json or downstream debate.
|
|
551
1047
|
*/
|
|
552
1048
|
export function workerFailureArtifacts({ task, symbol, asOfDate, language, timeoutMs, result, failureKind, parseError }) {
|
|
553
|
-
const
|
|
1049
|
+
const copy = localized(language, {
|
|
1050
|
+
en: {
|
|
1051
|
+
parse: `Evidence worker ${task} returned output that violated the JSON contract; it produced no evidence usable for an investment decision.`,
|
|
1052
|
+
language: `Evidence worker ${task} returned reader-facing content in the wrong language; it produced no evidence usable for an investment decision.`,
|
|
1053
|
+
failed: `Evidence worker ${task} timed out or failed; it produced no evidence usable for an investment decision.`,
|
|
1054
|
+
inspect: `Inspect the separate ${task} failure diagnostic and retry; do not use its partial transcript as evidence.`,
|
|
1055
|
+
},
|
|
1056
|
+
zh: {
|
|
1057
|
+
parse: `证据席位 ${task} 的输出不符合 JSON 契约;未生成可用于投资判断的证据。`,
|
|
1058
|
+
language: `证据席位 ${task} 返回了错误语言的读者内容;未生成可用于投资判断的证据。`,
|
|
1059
|
+
failed: `证据席位 ${task} 执行超时或失败;未生成可用于投资判断的证据。`,
|
|
1060
|
+
inspect: `检查 ${task} 的独立失败诊断并重试;不得用该席位的部分对话补齐证据。`,
|
|
1061
|
+
},
|
|
1062
|
+
ja: {
|
|
1063
|
+
parse: `証拠席 ${task} の出力は JSON 契約に違反しており、投資判断に使用できる証拠は生成されませんでした。`,
|
|
1064
|
+
language: `証拠席 ${task} は指定と異なる言語の読者向け内容を返したため、投資判断に使用できる証拠は生成されませんでした。`,
|
|
1065
|
+
failed: `証拠席 ${task} はタイムアウトまたは失敗し、投資判断に使用できる証拠は生成されませんでした。`,
|
|
1066
|
+
inspect: `${task} の分離された失敗診断を確認して再実行してください。部分的な対話を証拠として使用してはいけません。`,
|
|
1067
|
+
},
|
|
1068
|
+
ko: {
|
|
1069
|
+
parse: `증거 좌석 ${task}의 출력이 JSON 계약을 위반해 투자 판단에 사용할 수 있는 증거를 생성하지 못했습니다.`,
|
|
1070
|
+
language: `증거 좌석 ${task}가 지정과 다른 언어의 독자용 내용을 반환해 투자 판단에 사용할 수 있는 증거를 생성하지 못했습니다.`,
|
|
1071
|
+
failed: `증거 좌석 ${task}가 시간 초과 또는 실패로 투자 판단에 사용할 수 있는 증거를 생성하지 못했습니다.`,
|
|
1072
|
+
inspect: `${task}의 분리된 실패 진단을 확인한 뒤 다시 실행하십시오. 부분 대화를 증거로 사용해서는 안 됩니다.`,
|
|
1073
|
+
},
|
|
1074
|
+
});
|
|
554
1075
|
const timedOut = result?.timedOut === true;
|
|
555
1076
|
const parseFailed = failureKind === "parse_failed";
|
|
556
|
-
const
|
|
1077
|
+
const languageMismatch = failureKind === "reader_language_mismatch";
|
|
1078
|
+
const outputContractFailed = parseFailed || languageMismatch;
|
|
1079
|
+
const status = outputContractFailed ? failureKind : (timedOut ? "timed_out" : "failed");
|
|
557
1080
|
const exitLabel = Number.isInteger(result?.code) ? `exit code ${result.code}` : "worker error";
|
|
558
1081
|
const parseMessage = cleanLog(parseError?.message || parseError || "subagent did not return valid JSON", 1_000);
|
|
559
|
-
const reason =
|
|
1082
|
+
const reason = outputContractFailed ? parseMessage : (timedOut ? `timeout after ${timeoutMs}ms` : exitLabel);
|
|
560
1083
|
const rawOutput = String(result?.text || "");
|
|
561
|
-
const positionMatch =
|
|
1084
|
+
const positionMatch = outputContractFailed ? /\bposition\s+(\d+)\b/i.exec(parseMessage) : null;
|
|
562
1085
|
const parsePosition = positionMatch ? Number(positionMatch[1]) : null;
|
|
563
1086
|
const contextStart = Number.isInteger(parsePosition) ? Math.max(0, parsePosition - 500) : 0;
|
|
564
1087
|
const contextEnd = Number.isInteger(parsePosition) ? Math.min(rawOutput.length, parsePosition + 500) : 0;
|
|
565
1088
|
const packet = normalizePacket({
|
|
566
1089
|
summary: parseFailed
|
|
567
|
-
?
|
|
568
|
-
|
|
569
|
-
: `Evidence worker ${task} returned output that violated the JSON contract; it produced no evidence usable for an investment decision.`)
|
|
570
|
-
: (chinese
|
|
571
|
-
? `证据席位 ${task} 执行超时或失败;未生成可用于投资判断的证据。`
|
|
572
|
-
: `Evidence worker ${task} timed out or failed; it produced no evidence usable for an investment decision.`),
|
|
1090
|
+
? copy.parse
|
|
1091
|
+
: languageMismatch ? copy.language : copy.failed,
|
|
573
1092
|
claims: [],
|
|
574
|
-
open_questions: [
|
|
575
|
-
? `检查 ${task} 的独立失败诊断并重试;不得用该席位的部分对话补齐证据。`
|
|
576
|
-
: `Inspect the separate ${task} failure diagnostic and retry; do not use its partial transcript as evidence.`],
|
|
1093
|
+
open_questions: [copy.inspect],
|
|
577
1094
|
confidence: "low",
|
|
578
1095
|
}, task, symbol, asOfDate, "");
|
|
579
1096
|
const diagnostic = {
|
|
@@ -586,11 +1103,11 @@ export function workerFailureArtifacts({ task, symbol, asOfDate, language, timeo
|
|
|
586
1103
|
timeout_ms: timeoutMs,
|
|
587
1104
|
exit_code: Number.isInteger(result?.code) ? result.code : null,
|
|
588
1105
|
reason,
|
|
589
|
-
diagnostic_excerpt: cleanLog(
|
|
1106
|
+
diagnostic_excerpt: cleanLog(outputContractFailed
|
|
590
1107
|
? (rawOutput || result?.stderr || result?.stdout || reason)
|
|
591
1108
|
: (result?.stderr || result?.stdout || rawOutput || reason)),
|
|
592
|
-
...(
|
|
593
|
-
parse_error: parseMessage,
|
|
1109
|
+
...(outputContractFailed ? {
|
|
1110
|
+
...(parseFailed ? { parse_error: parseMessage } : { reader_language_error: parseMessage }),
|
|
594
1111
|
parse_position: Number.isInteger(parsePosition) ? parsePosition : null,
|
|
595
1112
|
parse_context: Number.isInteger(parsePosition)
|
|
596
1113
|
? cleanLog(rawOutput.slice(contextStart, contextEnd), 1_000)
|
|
@@ -603,6 +1120,68 @@ export function workerFailureArtifacts({ task, symbol, asOfDate, language, timeo
|
|
|
603
1120
|
return { packet, diagnostic };
|
|
604
1121
|
}
|
|
605
1122
|
|
|
1123
|
+
function evidenceReaderText(packet) {
|
|
1124
|
+
return [
|
|
1125
|
+
packet?.summary,
|
|
1126
|
+
...(packet?.claims || []).flatMap((claim) => [claim?.claim, claim?.evidence]),
|
|
1127
|
+
...(packet?.open_questions || []),
|
|
1128
|
+
].filter(Boolean).join("\n");
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function readerStrings(value, skipKeys = new Set()) {
|
|
1132
|
+
if (typeof value === "string") return value.trim() ? [value] : [];
|
|
1133
|
+
if (Array.isArray(value)) return value.flatMap((item) => readerStrings(item, skipKeys));
|
|
1134
|
+
if (!value || typeof value !== "object") return [];
|
|
1135
|
+
return Object.entries(value).flatMap(([key, item]) => (
|
|
1136
|
+
skipKeys.has(key) ? [] : readerStrings(item, skipKeys)
|
|
1137
|
+
));
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function visibleEvidenceReaderText(packet) {
|
|
1141
|
+
const machine = new Set([
|
|
1142
|
+
"task", "symbol", "as_of", "source_ids", "id", "url", "published_at", "retrieved_at",
|
|
1143
|
+
"confidence", "information_richness", "thread_id", "execution_mode", "raw_text",
|
|
1144
|
+
]);
|
|
1145
|
+
return [
|
|
1146
|
+
...readerStrings(packet, machine),
|
|
1147
|
+
].filter(Boolean).join("\n");
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
function masterReaderText(opinion) {
|
|
1151
|
+
const machine = new Set([
|
|
1152
|
+
"master", "symbol", "as_of", "stance", "source_ids", "confidence", "thread_id", "raw_text",
|
|
1153
|
+
"engine", "voice_status", "voice_language", "dedicated_worker", "runtime_provenance",
|
|
1154
|
+
]);
|
|
1155
|
+
return readerStrings(opinion, machine).join("\n");
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function debateReaderText(packet) {
|
|
1159
|
+
const machine = new Set([
|
|
1160
|
+
"role", "symbol", "as_of", "decision_available", "rating", "winner", "source_ids", "confidence",
|
|
1161
|
+
"thread_id", "execution_mode", "raw_text", "round", "debate_rounds",
|
|
1162
|
+
]);
|
|
1163
|
+
return readerStrings(packet, machine).join("\n");
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
function assertVisibleReaderLanguage(text, run, label) {
|
|
1167
|
+
try {
|
|
1168
|
+
return assertReaderLanguage(text, run.language, label);
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
if (error?.code !== "READER_LANGUAGE_MISMATCH") throw error;
|
|
1171
|
+
throw invalidParams(error.message, {
|
|
1172
|
+
reason: "READER_LANGUAGE_MISMATCH",
|
|
1173
|
+
label,
|
|
1174
|
+
...error.data,
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function outputFailureKind(error) {
|
|
1180
|
+
return error?.code === "READER_LANGUAGE_MISMATCH"
|
|
1181
|
+
? "reader_language_mismatch"
|
|
1182
|
+
: "parse_failed";
|
|
1183
|
+
}
|
|
1184
|
+
|
|
606
1185
|
/**
|
|
607
1186
|
* A production headless run must not silently become a prompt-only council because its
|
|
608
1187
|
* caller omitted `grounding`. Visible runs already collect it in rpc.mjs; this gives the
|
|
@@ -669,7 +1248,7 @@ export function queueHeadlessRun(args) {
|
|
|
669
1248
|
const dir = runPath(id);
|
|
670
1249
|
mkdirSync(dir, { recursive: true });
|
|
671
1250
|
const startedAt = new Date().toISOString();
|
|
672
|
-
const timing =
|
|
1251
|
+
const timing = councilTiming(args, startedAt);
|
|
673
1252
|
const run = {
|
|
674
1253
|
run_id: id,
|
|
675
1254
|
symbol,
|
|
@@ -723,21 +1302,32 @@ export async function collectEvidence(args) {
|
|
|
723
1302
|
const language = resolveLanguage(args);
|
|
724
1303
|
const frozen = frozenMasterSelection(args);
|
|
725
1304
|
const startedAt = args.queued_run?.started_at || new Date().toISOString();
|
|
726
|
-
const timing =
|
|
1305
|
+
const timing = councilTiming(args, startedAt);
|
|
727
1306
|
const requestedTimeoutMs = Number.isFinite(args.timeout_ms) ? args.timeout_ms : LIMITS.CODEX_TIMEOUT_MS;
|
|
728
1307
|
const timeoutMs = timing.council_mode === "quick"
|
|
729
1308
|
? Math.min(requestedTimeoutMs, LIMITS.QUICK_EVIDENCE_MS)
|
|
730
|
-
: requestedTimeoutMs;
|
|
731
|
-
const defaultConcurrency = timing.council_mode === "quick"
|
|
732
|
-
|
|
1309
|
+
: Math.min(requestedTimeoutMs, LIMITS.FULL_EVIDENCE_MS);
|
|
1310
|
+
const defaultConcurrency = timing.council_mode === "quick"
|
|
1311
|
+
? QUICK_TASKS.length
|
|
1312
|
+
: Math.min(tasks.length, LIMITS.FULL_EVIDENCE_CONCURRENCY);
|
|
1313
|
+
// The evidence topology is contractual: every planned evidence seat starts in the same
|
|
1314
|
+
// wave. A legacy low max_concurrency override may not silently turn the full fan-out back
|
|
1315
|
+
// into the old three-wave, ~30-minute evidence stage. Higher values have no effect once
|
|
1316
|
+
// every task can launch.
|
|
1317
|
+
const requestedConcurrency = Number(args.max_concurrency || defaultConcurrency);
|
|
1318
|
+
const maxConcurrency = Math.max(
|
|
1319
|
+
defaultConcurrency,
|
|
1320
|
+
Math.min(LIMITS.CONCURRENCY_MAX, Number.isFinite(requestedConcurrency) ? requestedConcurrency : defaultConcurrency),
|
|
1321
|
+
);
|
|
733
1322
|
const grounding = await groundingForHeadlessRun({
|
|
734
1323
|
symbol,
|
|
735
1324
|
asOf: asOfDate,
|
|
736
1325
|
grounding: args.grounding,
|
|
737
1326
|
dryRun,
|
|
738
|
-
timeoutMs:
|
|
739
|
-
|
|
740
|
-
:
|
|
1327
|
+
timeoutMs: remainingCouncilBudget(
|
|
1328
|
+
timing,
|
|
1329
|
+
timing.council_mode === "quick" ? LIMITS.QUICK_GROUNDING_MS : LIMITS.FULL_GROUNDING_MS,
|
|
1330
|
+
),
|
|
741
1331
|
});
|
|
742
1332
|
const dir = runPath(id);
|
|
743
1333
|
mkdirSync(dir, { recursive: true });
|
|
@@ -847,15 +1437,15 @@ export async function collectEvidence(args) {
|
|
|
847
1437
|
return packet;
|
|
848
1438
|
}
|
|
849
1439
|
const workerStartedAt = Date.now();
|
|
850
|
-
const runAttempt = async (workerPrompt, budgetMs, attempt) => {
|
|
851
|
-
const allowedMs =
|
|
852
|
-
if (allowedMs <= 0) return { ...deadlineResult(), budget_ms: 0 };
|
|
1440
|
+
const runAttempt = async (workerPrompt, budgetMs, attempt, { search = true } = {}) => {
|
|
1441
|
+
const allowedMs = remainingCouncilBudget(run, budgetMs);
|
|
1442
|
+
if (allowedMs <= 0) return { ...deadlineResult(run), budget_ms: 0 };
|
|
853
1443
|
const result = await runCodex(workerPrompt, allowedMs, ({ pid, output }) => {
|
|
854
1444
|
updateTask(run, task, "running", { pid, output, attempts: attempt });
|
|
855
1445
|
}, ({ pid, output, elapsed_ms }) => {
|
|
856
1446
|
updateTask(run, task, "running", { pid, output, attempts: attempt });
|
|
857
1447
|
appendEvent(run, "task_heartbeat", { task, pid, output, elapsed_ms, attempt });
|
|
858
|
-
});
|
|
1448
|
+
}, { search, sigkillGraceMs: councilKillGrace(run) });
|
|
859
1449
|
return { ...result, budget_ms: allowedMs };
|
|
860
1450
|
};
|
|
861
1451
|
const commitFailure = ({ failedResult, budgetMs, attempts, failureKind, parseError, retryDiagnostic }) => {
|
|
@@ -882,8 +1472,8 @@ export async function collectEvidence(args) {
|
|
|
882
1472
|
...(retryDiagnostic ? { retry_diagnostic: retryDiagnostic } : {}),
|
|
883
1473
|
attempts,
|
|
884
1474
|
deadline_exhausted: failedResult.deadline_exhausted === true,
|
|
885
|
-
error:
|
|
886
|
-
?
|
|
1475
|
+
error: ["parse_failed", "reader_language_mismatch"].includes(failureKind)
|
|
1476
|
+
? failureKind
|
|
887
1477
|
: (failedResult.deadline_exhausted ? "global_deadline" : failedResult.timedOut ? "timeout" : `exit code ${failedResult.code}`),
|
|
888
1478
|
});
|
|
889
1479
|
return failure.packet;
|
|
@@ -896,10 +1486,12 @@ export async function collectEvidence(args) {
|
|
|
896
1486
|
let packet;
|
|
897
1487
|
try {
|
|
898
1488
|
packet = normalizePacket(extractJson(result.text), task, symbol, asOfDate, result.text);
|
|
1489
|
+
assertReaderLanguage(evidenceReaderText(packet), language, `evidence worker ${task}`);
|
|
899
1490
|
commitPacket(packet);
|
|
900
1491
|
updateTask(run, task, "completed", { completed_at: new Date().toISOString(), output: join(dir, `${task}.json`), attempts: 1 });
|
|
901
1492
|
return packet;
|
|
902
1493
|
} catch (firstParseError) {
|
|
1494
|
+
const firstFailureKind = outputFailureKind(firstParseError);
|
|
903
1495
|
const firstFailure = workerFailureArtifacts({
|
|
904
1496
|
task,
|
|
905
1497
|
symbol,
|
|
@@ -907,19 +1499,23 @@ export async function collectEvidence(args) {
|
|
|
907
1499
|
language,
|
|
908
1500
|
timeoutMs,
|
|
909
1501
|
result,
|
|
910
|
-
failureKind:
|
|
1502
|
+
failureKind: firstFailureKind,
|
|
911
1503
|
parseError: firstParseError,
|
|
912
1504
|
});
|
|
913
1505
|
const retryDiagnostic = join(dir, `${task}.attempt-1.failure.json`);
|
|
914
1506
|
writeJson(retryDiagnostic, firstFailure.diagnostic, { mode: 0o600 });
|
|
915
1507
|
const elapsedMs = Date.now() - workerStartedAt;
|
|
916
|
-
const retryTimeoutMs = Math.min(
|
|
1508
|
+
const retryTimeoutMs = Math.min(
|
|
1509
|
+
LIMITS.PARSE_REPAIR_MS,
|
|
1510
|
+
timeoutMs - elapsedMs,
|
|
1511
|
+
remainingCouncilBudget(run, LIMITS.PARSE_REPAIR_MS),
|
|
1512
|
+
);
|
|
917
1513
|
if (retryTimeoutMs <= 0) {
|
|
918
1514
|
return commitFailure({
|
|
919
1515
|
failedResult: result,
|
|
920
1516
|
budgetMs: result.budget_ms ?? timeoutMs,
|
|
921
1517
|
attempts: 1,
|
|
922
|
-
failureKind:
|
|
1518
|
+
failureKind: firstFailureKind,
|
|
923
1519
|
parseError: firstParseError,
|
|
924
1520
|
retryDiagnostic,
|
|
925
1521
|
});
|
|
@@ -929,13 +1525,22 @@ export async function collectEvidence(args) {
|
|
|
929
1525
|
task,
|
|
930
1526
|
attempt: 2,
|
|
931
1527
|
max_attempts: 2,
|
|
932
|
-
reason:
|
|
1528
|
+
reason: firstFailureKind,
|
|
933
1529
|
retry_diagnostic: retryDiagnostic,
|
|
934
1530
|
remaining_ms: retryTimeoutMs,
|
|
935
1531
|
});
|
|
936
1532
|
updateTask(run, task, "running", { attempts: 2, retry_diagnostic: retryDiagnostic });
|
|
937
|
-
const
|
|
938
|
-
|
|
1533
|
+
const malformed = String(result.text || "").slice(0, LIMITS.PARSE_REPAIR_INPUT_CHARS);
|
|
1534
|
+
const retryPrompt = [
|
|
1535
|
+
"PARSE-ONLY TRANSPORT REPAIR. Do not search, browse, fetch, add facts, or redo the research.",
|
|
1536
|
+
`Target task: ${task}; symbol: ${symbol}; as_of: ${asOfDate}; reader language: ${language}.`,
|
|
1537
|
+
firstFailureKind === "reader_language_mismatch"
|
|
1538
|
+
? "Translate only the reader-facing strings in the supplied valid JSON into the requested language. Preserve the evidence packet schema, claims, numbers, source IDs, URLs, dates, explicit gaps and uncertainty. Return exactly one JSON object and nothing else."
|
|
1539
|
+
: "Convert only the supplied malformed worker output into exactly one valid JSON object matching the evidence packet schema. Preserve claims, numbers, source IDs, URLs, dates, explicit gaps and uncertainty. If a field cannot be recovered, use an empty value and record the loss in open_questions. Return JSON only.",
|
|
1540
|
+
`Write every reader-facing value in ${language}. Translation is permitted only to repair the language mismatch; do not alter facts, numbers, dates, source IDs or uncertainty.`,
|
|
1541
|
+
`Malformed worker output:\n${malformed}`,
|
|
1542
|
+
].join("\n\n");
|
|
1543
|
+
result = await runAttempt(retryPrompt, retryTimeoutMs, 2, { search: false });
|
|
939
1544
|
if (!result.ok) {
|
|
940
1545
|
return commitFailure({
|
|
941
1546
|
failedResult: result,
|
|
@@ -946,6 +1551,7 @@ export async function collectEvidence(args) {
|
|
|
946
1551
|
}
|
|
947
1552
|
try {
|
|
948
1553
|
packet = normalizePacket(extractJson(result.text), task, symbol, asOfDate, result.text);
|
|
1554
|
+
assertReaderLanguage(evidenceReaderText(packet), language, `evidence worker ${task} repair`);
|
|
949
1555
|
commitPacket(packet);
|
|
950
1556
|
updateTask(run, task, "completed", {
|
|
951
1557
|
completed_at: new Date().toISOString(),
|
|
@@ -955,11 +1561,12 @@ export async function collectEvidence(args) {
|
|
|
955
1561
|
});
|
|
956
1562
|
return packet;
|
|
957
1563
|
} catch (secondParseError) {
|
|
1564
|
+
const secondFailureKind = outputFailureKind(secondParseError);
|
|
958
1565
|
return commitFailure({
|
|
959
1566
|
failedResult: result,
|
|
960
1567
|
budgetMs: result.budget_ms ?? retryTimeoutMs,
|
|
961
1568
|
attempts: 2,
|
|
962
|
-
failureKind:
|
|
1569
|
+
failureKind: secondFailureKind,
|
|
963
1570
|
parseError: secondParseError,
|
|
964
1571
|
retryDiagnostic,
|
|
965
1572
|
});
|
|
@@ -1022,11 +1629,14 @@ export async function runHeadlessMasters(run, args = {}) {
|
|
|
1022
1629
|
const requestedTimeoutMs = Number.isFinite(args.timeout_ms) ? args.timeout_ms : LIMITS.CODEX_TIMEOUT_MS;
|
|
1023
1630
|
const timeoutMs = run.council_mode === "quick"
|
|
1024
1631
|
? Math.min(requestedTimeoutMs, LIMITS.QUICK_MASTER_MS)
|
|
1025
|
-
: requestedTimeoutMs;
|
|
1026
|
-
const defaultConcurrency = run.council_mode === "quick"
|
|
1632
|
+
: Math.min(requestedTimeoutMs, LIMITS.FULL_MASTER_MS);
|
|
1633
|
+
const defaultConcurrency = run.council_mode === "quick"
|
|
1634
|
+
? 4
|
|
1635
|
+
: Math.min(selected.length, LIMITS.FULL_MASTER_CONCURRENCY);
|
|
1636
|
+
const requestedConcurrency = Number(args.max_concurrency || defaultConcurrency);
|
|
1027
1637
|
const maxConcurrency = Math.max(
|
|
1028
|
-
|
|
1029
|
-
Math.min(LIMITS.CONCURRENCY_MAX, Number(
|
|
1638
|
+
defaultConcurrency,
|
|
1639
|
+
Math.min(LIMITS.CONCURRENCY_MAX, Number.isFinite(requestedConcurrency) ? requestedConcurrency : defaultConcurrency),
|
|
1030
1640
|
);
|
|
1031
1641
|
const plan = planMasterSeats(run, selected);
|
|
1032
1642
|
const byId = new Map((run.master_opinions || []).map((opinion) => [opinion.master, opinion]));
|
|
@@ -1045,40 +1655,6 @@ export async function runHeadlessMasters(run, args = {}) {
|
|
|
1045
1655
|
blocked: plan.blocked.length,
|
|
1046
1656
|
});
|
|
1047
1657
|
|
|
1048
|
-
for (const item of plan.declined) {
|
|
1049
|
-
const opinion = attachMasterRuntimeProvenance(
|
|
1050
|
-
run,
|
|
1051
|
-
item.id,
|
|
1052
|
-
declinedMasterOpinion(run, item),
|
|
1053
|
-
item.engine,
|
|
1054
|
-
);
|
|
1055
|
-
byId.set(item.id, opinion);
|
|
1056
|
-
writeJson(join(dir, `${item.id}.json`), opinion);
|
|
1057
|
-
updateMasterStatus(run, item.id, "completed", {
|
|
1058
|
-
engine: item.engine || opinion.engine,
|
|
1059
|
-
deterministic_decline: true,
|
|
1060
|
-
completed_at: new Date().toISOString(),
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
for (const item of plan.completed) {
|
|
1065
|
-
const opinion = attachMasterRuntimeProvenance(
|
|
1066
|
-
run,
|
|
1067
|
-
item.id,
|
|
1068
|
-
completedMasterOpinion(run, item),
|
|
1069
|
-
item.engine,
|
|
1070
|
-
);
|
|
1071
|
-
byId.set(item.id, opinion);
|
|
1072
|
-
writeJson(join(dir, `${item.id}.json`), opinion);
|
|
1073
|
-
updateMasterStatus(run, item.id, "completed", {
|
|
1074
|
-
engine: item.engine,
|
|
1075
|
-
deterministic_execution: true,
|
|
1076
|
-
policy_execution_hash: opinion.policy_execution_hash,
|
|
1077
|
-
frozen_decision_hash: opinion.frozen_decision_hash,
|
|
1078
|
-
completed_at: new Date().toISOString(),
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
1658
|
for (const item of plan.blocked) {
|
|
1083
1659
|
updateMasterStatus(run, item.id, "failed", {
|
|
1084
1660
|
engine: item.engine,
|
|
@@ -1087,48 +1663,155 @@ export async function runHeadlessMasters(run, args = {}) {
|
|
|
1087
1663
|
});
|
|
1088
1664
|
}
|
|
1089
1665
|
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1666
|
+
const workerItems = [
|
|
1667
|
+
...plan.declined.map((item) => ({
|
|
1668
|
+
...item,
|
|
1669
|
+
frozenOpinion: attachMasterRuntimeProvenance(run, item.id, declinedMasterOpinion(run, item), item.engine),
|
|
1670
|
+
deterministic_decline: true,
|
|
1671
|
+
})),
|
|
1672
|
+
...plan.completed.map((item) => ({
|
|
1673
|
+
...item,
|
|
1674
|
+
frozenOpinion: attachMasterRuntimeProvenance(run, item.id, completedMasterOpinion(run, item), item.engine),
|
|
1675
|
+
deterministic_execution: true,
|
|
1676
|
+
})),
|
|
1677
|
+
...plan.to_run,
|
|
1678
|
+
];
|
|
1679
|
+
for (const item of workerItems) {
|
|
1680
|
+
if (!item.frozenOpinion) continue;
|
|
1681
|
+
writeJson(join(dir, `${item.id}.deterministic.json`), item.frozenOpinion);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
const outcomes = await mapLimit(workerItems, maxConcurrency, async ({ id, decision, engine, frozenOpinion, deterministic_decline, deterministic_execution }) => {
|
|
1685
|
+
const prompt = frozenOpinion
|
|
1686
|
+
? masterVoicePrompt(id, run, frozenOpinion)
|
|
1687
|
+
: [
|
|
1688
|
+
masterPrompt(id, run),
|
|
1689
|
+
decision ? deterministicVerdictBlock(decision, isChineseLanguage(run.language)) : "",
|
|
1690
|
+
].filter(Boolean).join("\n\n");
|
|
1691
|
+
updateMasterStatus(run, id, "running", {
|
|
1692
|
+
started_at: new Date().toISOString(),
|
|
1693
|
+
worker_kind: frozenOpinion ? "dedicated_method_voice" : "dedicated_method_judgment",
|
|
1694
|
+
deterministic_decline: deterministic_decline || undefined,
|
|
1695
|
+
deterministic_execution: deterministic_execution || undefined,
|
|
1696
|
+
});
|
|
1096
1697
|
|
|
1097
1698
|
if (run.dry_run) {
|
|
1699
|
+
if (frozenOpinion) {
|
|
1700
|
+
return {
|
|
1701
|
+
id,
|
|
1702
|
+
engine,
|
|
1703
|
+
opinion: {
|
|
1704
|
+
...frozenOpinion,
|
|
1705
|
+
deterministic_summary: frozenOpinion.summary,
|
|
1706
|
+
voice_statement: frozenOpinion.summary,
|
|
1707
|
+
voice_status: "dry_run",
|
|
1708
|
+
dedicated_worker: { status: "dry_run", language: run.language, execution_mode: "dry_run" },
|
|
1709
|
+
},
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1098
1712
|
const normalized = normalizeMasterOpinion({
|
|
1099
1713
|
verdict: "DRY_RUN",
|
|
1100
1714
|
stance: "out_of_scope",
|
|
1101
|
-
summary: "Dry run: the
|
|
1715
|
+
summary: "Dry run: the dedicated method worker was planned but no model judgment was executed.",
|
|
1102
1716
|
what_would_change_my_mind: ["Run without dry_run to obtain a method judgment."],
|
|
1103
1717
|
confidence: "low",
|
|
1104
1718
|
}, id, run, prompt);
|
|
1105
1719
|
const reconciled = reconcileMasterOpinion(run, id, normalized);
|
|
1106
1720
|
const resolvedEngine = engine || reconciled.engine || (decision ? "v2_method_model" : "v1_prompt");
|
|
1107
|
-
return { id, opinion: attachMasterRuntimeProvenance(run, id, reconciled.opinion, resolvedEngine), engine: resolvedEngine };
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
const allowedMs = remainingQuickBudget(run, timeoutMs);
|
|
1111
|
-
const result = allowedMs <= 0
|
|
1112
|
-
? deadlineResult()
|
|
1113
|
-
: await runCodex(prompt, allowedMs, ({ pid, output }) => {
|
|
1114
|
-
updateMasterStatus(run, id, "running", { pid, output });
|
|
1115
|
-
}, ({ pid, output, elapsed_ms }) => {
|
|
1116
|
-
updateMasterStatus(run, id, "running", { pid, output, elapsed_ms });
|
|
1117
|
-
});
|
|
1118
|
-
if (!result.ok) {
|
|
1119
1721
|
return {
|
|
1120
1722
|
id,
|
|
1121
|
-
|
|
1122
|
-
|
|
1723
|
+
opinion: attachMasterRuntimeProvenance(run, id, {
|
|
1724
|
+
...reconciled.opinion,
|
|
1725
|
+
voice_status: "dry_run",
|
|
1726
|
+
dedicated_worker: { status: "dry_run", language: run.language, execution_mode: "dry_run" },
|
|
1727
|
+
}, resolvedEngine),
|
|
1728
|
+
engine: resolvedEngine,
|
|
1123
1729
|
};
|
|
1124
1730
|
}
|
|
1125
|
-
|
|
1731
|
+
|
|
1732
|
+
let pid = null;
|
|
1733
|
+
const execute = async (workerPrompt, budgetMs, attempt) => {
|
|
1734
|
+
const allowedMs = remainingCouncilBudget(run, budgetMs);
|
|
1735
|
+
if (allowedMs <= 0) return { ...deadlineResult(run), budget_ms: 0 };
|
|
1736
|
+
const result = await runCodex(workerPrompt, allowedMs, ({ pid: workerPid, output }) => {
|
|
1737
|
+
pid = workerPid;
|
|
1738
|
+
updateMasterStatus(run, id, "running", { pid: workerPid, output, attempts: attempt });
|
|
1739
|
+
}, ({ pid: workerPid, output, elapsed_ms }) => {
|
|
1740
|
+
updateMasterStatus(run, id, "running", { pid: workerPid, output, elapsed_ms, attempts: attempt });
|
|
1741
|
+
}, { search: false, sigkillGraceMs: councilKillGrace(run) });
|
|
1742
|
+
return { ...result, budget_ms: allowedMs };
|
|
1743
|
+
};
|
|
1744
|
+
const parse = (result) => {
|
|
1745
|
+
if (frozenOpinion) {
|
|
1746
|
+
const voice = normalizeMasterVoice(extractJson(result.text), id, run, frozenOpinion, result.text);
|
|
1747
|
+
assertReaderLanguage([
|
|
1748
|
+
voice.statement,
|
|
1749
|
+
...(voice.key_findings || []),
|
|
1750
|
+
...(voice.disagreements || []),
|
|
1751
|
+
...(voice.what_would_change_my_mind || []),
|
|
1752
|
+
].filter(Boolean).join("\n"), run.language, `master voice ${id}`);
|
|
1753
|
+
return attachMasterRuntimeProvenance(run, id, {
|
|
1754
|
+
...frozenOpinion,
|
|
1755
|
+
deterministic_summary: frozenOpinion.summary,
|
|
1756
|
+
summary: voice.statement,
|
|
1757
|
+
voice_statement: voice.statement,
|
|
1758
|
+
voice_status: "completed",
|
|
1759
|
+
voice_language: run.language,
|
|
1760
|
+
key_findings: voice.key_findings.length ? voice.key_findings : frozenOpinion.key_findings,
|
|
1761
|
+
disagreements: voice.disagreements,
|
|
1762
|
+
what_would_change_my_mind: voice.what_would_change_my_mind.length
|
|
1763
|
+
? voice.what_would_change_my_mind
|
|
1764
|
+
: frozenOpinion.what_would_change_my_mind,
|
|
1765
|
+
source_ids: [...new Set([...(frozenOpinion.source_ids || []), ...voice.source_ids])],
|
|
1766
|
+
confidence: voice.confidence,
|
|
1767
|
+
dedicated_worker: { status: "completed", pid, language: run.language, execution_mode: "codex_exec" },
|
|
1768
|
+
}, engine);
|
|
1769
|
+
}
|
|
1126
1770
|
const normalized = normalizeMasterOpinion(extractJson(result.text), id, run, result.text);
|
|
1771
|
+
assertReaderLanguage([
|
|
1772
|
+
normalized.summary,
|
|
1773
|
+
normalized.verdict,
|
|
1774
|
+
...(normalized.key_findings || []),
|
|
1775
|
+
...(normalized.disagreements || []),
|
|
1776
|
+
...(normalized.what_would_change_my_mind || []),
|
|
1777
|
+
].filter(Boolean).join("\n"), run.language, `legacy master voice ${id}`);
|
|
1127
1778
|
const reconciled = reconcileMasterOpinion(run, id, normalized);
|
|
1128
1779
|
const resolvedEngine = engine || reconciled.engine || (decision ? "v2_method_model" : "v1_prompt");
|
|
1129
|
-
return
|
|
1130
|
-
|
|
1131
|
-
|
|
1780
|
+
return attachMasterRuntimeProvenance(run, id, {
|
|
1781
|
+
...reconciled.opinion,
|
|
1782
|
+
voice_statement: reconciled.opinion.summary || reconciled.opinion.verdict,
|
|
1783
|
+
voice_status: "completed",
|
|
1784
|
+
voice_language: run.language,
|
|
1785
|
+
dedicated_worker: { status: "completed", pid, language: run.language, execution_mode: "codex_exec" },
|
|
1786
|
+
}, resolvedEngine);
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
let result = await execute(prompt, timeoutMs, 1);
|
|
1790
|
+
if (!result.ok) {
|
|
1791
|
+
return { id, error: result.deadline_exhausted ? "global_deadline" : result.timedOut ? "timeout" : `exit code ${result.code}`, raw: cleanLog(result.stderr || result.stdout || "method worker failed") };
|
|
1792
|
+
}
|
|
1793
|
+
try {
|
|
1794
|
+
return { id, opinion: parse(result), engine };
|
|
1795
|
+
} catch (firstParseError) {
|
|
1796
|
+
const firstFailureKind = outputFailureKind(firstParseError);
|
|
1797
|
+
const repairBudget = Math.min(LIMITS.PARSE_REPAIR_MS, remainingCouncilBudget(run, LIMITS.PARSE_REPAIR_MS));
|
|
1798
|
+
if (repairBudget <= 0) return { id, error: firstFailureKind, raw: cleanLog(firstParseError?.message || result.text) };
|
|
1799
|
+
const repairPrompt = [
|
|
1800
|
+
"PARSE-ONLY TRANSPORT REPAIR. Do not browse, search, add facts or change the frozen method stance.",
|
|
1801
|
+
`Master ID: ${id}; required acknowledged stance: ${frozenOpinion?.stance || "use the original stance"}; output language: ${run.language}.`,
|
|
1802
|
+
frozenOpinion
|
|
1803
|
+
? "Return one JSON object with master, acknowledged_stance, statement, key_findings, disagreements, what_would_change_my_mind, source_ids and confidence."
|
|
1804
|
+
: "Return one JSON object matching the master_opinion schema from the original prompt.",
|
|
1805
|
+
`Write every reader-facing value in ${run.language}. Translation is allowed only to repair language; preserve the frozen stance, facts, numbers and source IDs.`,
|
|
1806
|
+
`Malformed output:\n${String(result.text || "").slice(0, LIMITS.PARSE_REPAIR_INPUT_CHARS)}`,
|
|
1807
|
+
].join("\n\n");
|
|
1808
|
+
result = await execute(repairPrompt, repairBudget, 2);
|
|
1809
|
+
if (!result.ok) return { id, error: result.deadline_exhausted ? "global_deadline" : result.timedOut ? "timeout" : `exit code ${result.code}`, raw: cleanLog(result.stderr || "method repair failed") };
|
|
1810
|
+
try {
|
|
1811
|
+
return { id, opinion: parse(result), engine };
|
|
1812
|
+
} catch (secondParseError) {
|
|
1813
|
+
return { id, error: outputFailureKind(secondParseError), raw: cleanLog(secondParseError?.message || result.text) };
|
|
1814
|
+
}
|
|
1132
1815
|
}
|
|
1133
1816
|
}, (error, { id }) => ({
|
|
1134
1817
|
id,
|
|
@@ -1138,13 +1821,37 @@ export async function runHeadlessMasters(run, args = {}) {
|
|
|
1138
1821
|
|
|
1139
1822
|
for (const outcome of outcomes) {
|
|
1140
1823
|
if (!outcome.opinion) {
|
|
1141
|
-
|
|
1824
|
+
const diagnosticPath = join(dir, `${outcome.id}.failure.json`);
|
|
1825
|
+
writeJson(diagnosticPath, {
|
|
1826
|
+
master: outcome.id,
|
|
1827
|
+
failure_kind: outcome.error,
|
|
1828
|
+
diagnostic: outcome.raw,
|
|
1829
|
+
public_summary: outcome.error === "reader_language_mismatch"
|
|
1830
|
+
? localized(run.language, {
|
|
1831
|
+
en: "The dedicated method worker returned reader-facing content in the wrong language; no method-seat statement is available.",
|
|
1832
|
+
zh: "专属方法席 worker 返回了错误语言的读者内容;没有可用的方法席发言。",
|
|
1833
|
+
ja: "専用メソッド席ワーカーは指定と異なる言語の読者向け内容を返したため、利用可能なメソッド席の発言はありません。",
|
|
1834
|
+
ko: "전용 방법론 좌석 워커가 지정과 다른 언어의 독자용 내용을 반환해 사용할 수 있는 방법론 좌석 발언이 없습니다.",
|
|
1835
|
+
})
|
|
1836
|
+
: localized(run.language, {
|
|
1837
|
+
en: "The dedicated method worker did not complete; no method-seat statement is available.",
|
|
1838
|
+
zh: "专属方法席 worker 未完成;没有可用的方法席发言。",
|
|
1839
|
+
ja: "専用メソッド席ワーカーが完了せず、利用可能なメソッド席の発言はありません。",
|
|
1840
|
+
ko: "전용 방법론 좌석 워커가 완료되지 않아 사용할 수 있는 방법론 좌석 발언이 없습니다.",
|
|
1841
|
+
}),
|
|
1842
|
+
}, { mode: 0o600 });
|
|
1843
|
+
updateMasterStatus(run, outcome.id, "failed", { error: outcome.error, diagnostic: diagnosticPath });
|
|
1142
1844
|
continue;
|
|
1143
1845
|
}
|
|
1144
1846
|
byId.set(outcome.id, outcome.opinion);
|
|
1145
1847
|
writeJson(join(dir, `${outcome.id}.json`), outcome.opinion);
|
|
1146
1848
|
updateMasterStatus(run, outcome.id, "completed", {
|
|
1147
1849
|
engine: outcome.opinion.engine || outcome.engine,
|
|
1850
|
+
worker_kind: outcome.opinion.dedicated_worker?.execution_mode === "dry_run"
|
|
1851
|
+
? "dedicated_method_voice_dry_run"
|
|
1852
|
+
: "dedicated_method_worker",
|
|
1853
|
+
worker_pid: outcome.opinion.dedicated_worker?.pid || null,
|
|
1854
|
+
voice_status: outcome.opinion.voice_status || "completed",
|
|
1148
1855
|
completed_at: new Date().toISOString(),
|
|
1149
1856
|
output: join(dir, `${outcome.id}.json`),
|
|
1150
1857
|
});
|
|
@@ -1162,50 +1869,112 @@ export async function runHeadlessMasters(run, args = {}) {
|
|
|
1162
1869
|
export async function runDebateRole(run, role, context, timeoutMs) {
|
|
1163
1870
|
const prompt = debatePrompt(role, run, context);
|
|
1164
1871
|
updateAgent(run, role, "running", { started_at: new Date().toISOString(), round: context.round });
|
|
1165
|
-
|
|
1166
|
-
? deadlineResult()
|
|
1872
|
+
let result = timeoutMs <= 0
|
|
1873
|
+
? deadlineResult(run)
|
|
1167
1874
|
: await runCodex(prompt, timeoutMs, ({ pid, output }) => {
|
|
1168
1875
|
updateAgent(run, role, "running", { pid, output, round: context.round });
|
|
1169
1876
|
}, ({ pid, output, elapsed_ms }) => {
|
|
1170
1877
|
updateAgent(run, role, "running", { pid, output, round: context.round });
|
|
1171
1878
|
appendEvent(run, "agent_heartbeat", { role, round: context.round, pid, output, elapsed_ms });
|
|
1172
|
-
});
|
|
1173
|
-
const
|
|
1879
|
+
}, { search: false, sigkillGraceMs: councilKillGrace(run) });
|
|
1880
|
+
const enforceLanguage = (candidate) => {
|
|
1881
|
+
if (candidate?.failure_kind) return candidate;
|
|
1882
|
+
try {
|
|
1883
|
+
assertReaderLanguage([
|
|
1884
|
+
candidate?.summary,
|
|
1885
|
+
...(candidate?.long_thesis || []),
|
|
1886
|
+
...(candidate?.short_thesis || []),
|
|
1887
|
+
...(candidate?.catalysts || []),
|
|
1888
|
+
...(candidate?.risks || []),
|
|
1889
|
+
candidate?.position,
|
|
1890
|
+
...(candidate?.invalidation || []),
|
|
1891
|
+
...(candidate?.questions || []),
|
|
1892
|
+
...(candidate?.questions_answered || []).flatMap((item) => [item?.question, item?.answer]),
|
|
1893
|
+
candidate?.report_markdown,
|
|
1894
|
+
].filter(Boolean).join("\n"), run.language, `${role} debate output`);
|
|
1895
|
+
return candidate;
|
|
1896
|
+
} catch (error) {
|
|
1897
|
+
return debateFailurePacket(role, run, "reader_language_mismatch");
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
let packet = enforceLanguage(debateFromCodex(result, role, run, prompt));
|
|
1901
|
+
if (result.ok && ["parse_failed", "reader_language_mismatch"].includes(packet.failure_kind)) {
|
|
1902
|
+
const repairBudget = Math.min(LIMITS.PARSE_REPAIR_MS, remainingCouncilBudget(run, LIMITS.PARSE_REPAIR_MS));
|
|
1903
|
+
if (repairBudget > 0) {
|
|
1904
|
+
const repairReason = packet.failure_kind;
|
|
1905
|
+
appendEvent(run, "agent_parse_repair", { role, round: context.round, budget_ms: repairBudget, reason: repairReason });
|
|
1906
|
+
const repairPrompt = [
|
|
1907
|
+
"PARSE-ONLY TRANSPORT REPAIR. Do not search, browse, add facts or redo the analysis.",
|
|
1908
|
+
`Role: ${role}; symbol: ${run.symbol}; as_of: ${run.as_of}; reader language: ${run.language}; round: ${context.round || "final"}.`,
|
|
1909
|
+
repairReason === "reader_language_mismatch"
|
|
1910
|
+
? "Translate only the reader-facing strings in the supplied valid debate-packet JSON. Preserve exact round-2 questions, exact round-3 question bindings, facts, numbers, source IDs and uncertainty. Return one JSON object only."
|
|
1911
|
+
: "Convert only the supplied malformed output into one valid debate-packet JSON object. Preserve exact round-2 questions and exact round-3 question bindings when present. Return JSON only.",
|
|
1912
|
+
`Write every reader-facing value in ${run.language}. Translation is allowed only to repair language; preserve facts, numbers, source IDs and exact Q&A bindings.`,
|
|
1913
|
+
`Malformed output:\n${String(result.text || "").slice(0, LIMITS.PARSE_REPAIR_INPUT_CHARS)}`,
|
|
1914
|
+
].join("\n\n");
|
|
1915
|
+
result = await runCodex(repairPrompt, repairBudget, ({ pid, output }) => {
|
|
1916
|
+
updateAgent(run, role, "running", { pid, output, round: context.round, attempts: 2 });
|
|
1917
|
+
}, () => {}, { search: false, sigkillGraceMs: councilKillGrace(run) });
|
|
1918
|
+
packet = enforceLanguage(debateFromCodex(result, role, run, repairPrompt));
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1174
1921
|
const roundCompletedAt = new Date().toISOString();
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1922
|
+
if (Number.isInteger(context.round)) {
|
|
1923
|
+
// A role can run three times. Leaving it marked `running` after one awaited invocation
|
|
1924
|
+
// returned made sequential rounds look concurrent in status.json. Record the dependency
|
|
1925
|
+
// boundary explicitly while reserving `completed` for the merged three-round artifact.
|
|
1926
|
+
updateAgent(run, role, "waiting", {
|
|
1927
|
+
round: context.round,
|
|
1928
|
+
round_status: "completed",
|
|
1929
|
+
last_completed_round: context.round,
|
|
1930
|
+
round_completed_at: roundCompletedAt,
|
|
1931
|
+
pid: null,
|
|
1932
|
+
output: null,
|
|
1933
|
+
});
|
|
1934
|
+
appendEvent(run, "agent_round_completed", {
|
|
1935
|
+
role,
|
|
1936
|
+
round: context.round,
|
|
1937
|
+
ok: result.ok,
|
|
1938
|
+
timed_out: result.timedOut === true,
|
|
1939
|
+
verdict: packet.verdict,
|
|
1940
|
+
failure_kind: packet.failure_kind,
|
|
1941
|
+
question_count: packet.questions.length,
|
|
1942
|
+
answered_count: packet.questions_answered.length,
|
|
1943
|
+
});
|
|
1944
|
+
} else {
|
|
1945
|
+
updateAgent(run, role, "waiting", {
|
|
1946
|
+
synthesis_status: "completed",
|
|
1947
|
+
synthesis_completed_at: roundCompletedAt,
|
|
1948
|
+
pid: null,
|
|
1949
|
+
output: null,
|
|
1950
|
+
});
|
|
1951
|
+
appendEvent(run, "agent_role_completed", {
|
|
1952
|
+
role,
|
|
1953
|
+
ok: result.ok,
|
|
1954
|
+
timed_out: result.timedOut === true,
|
|
1955
|
+
verdict: packet.verdict,
|
|
1956
|
+
failure_kind: packet.failure_kind,
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1195
1959
|
return { packet, result };
|
|
1196
1960
|
}
|
|
1197
1961
|
|
|
1198
1962
|
function debateFailure(step) {
|
|
1199
|
-
if (step.result.ok
|
|
1963
|
+
if (!step.result.ok) {
|
|
1964
|
+
if (step.result.deadline_exhausted) return "global_deadline";
|
|
1965
|
+
if (step.result.timedOut) return "timeout";
|
|
1966
|
+
if (Number.isInteger(step.result.code)) return `exit code ${step.result.code}`;
|
|
1967
|
+
return "unexpected_error";
|
|
1968
|
+
}
|
|
1969
|
+
if (step.packet.failure_kind) return step.packet.failure_kind;
|
|
1200
1970
|
if (step.packet.verdict === "PARSE_FAILED") return "parse_failed";
|
|
1201
|
-
|
|
1202
|
-
return step.result.timedOut ? "timeout" : `exit code ${step.result.code ?? "unknown"}`;
|
|
1971
|
+
return undefined;
|
|
1203
1972
|
}
|
|
1204
1973
|
|
|
1205
1974
|
async function synthesizeQuickDecision(run, args, timeoutMs, outputMode) {
|
|
1206
1975
|
const dir = runPath(run.run_id);
|
|
1207
1976
|
appendEvent(run, "debate_round", { round: 1, format: "single_round_parallel" });
|
|
1208
|
-
const sideBudget =
|
|
1977
|
+
const sideBudget = remainingCouncilBudget(run, timeoutMs);
|
|
1209
1978
|
const [bullOutcome, bearOutcome] = await Promise.allSettled([
|
|
1210
1979
|
runDebateRole(run, "bull_researcher", { round: 1, brief: "quick long case" }, sideBudget),
|
|
1211
1980
|
runDebateRole(run, "bear_researcher", { round: 1, brief: "quick short case" }, sideBudget),
|
|
@@ -1249,21 +2018,11 @@ async function synthesizeQuickDecision(run, args, timeoutMs, outputMode) {
|
|
|
1249
2018
|
});
|
|
1250
2019
|
writeAllAgentsMarkdown(run, { bull, bear });
|
|
1251
2020
|
|
|
1252
|
-
const
|
|
1253
|
-
|
|
1254
|
-
const
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
: await runCodex(managerPrompt, managerBudget, ({ pid, output }) => {
|
|
1258
|
-
updateAgent(run, "portfolio_manager", "running", { pid, output });
|
|
1259
|
-
}, ({ pid, output, elapsed_ms }) => {
|
|
1260
|
-
updateAgent(run, "portfolio_manager", "running", { pid, output });
|
|
1261
|
-
appendEvent(run, "agent_heartbeat", { role: "portfolio_manager", pid, output, elapsed_ms });
|
|
1262
|
-
});
|
|
1263
|
-
const manager = managerResult.ok
|
|
1264
|
-
? debateFromCodex(managerResult, "portfolio_manager", run, managerPrompt)
|
|
1265
|
-
: managerFallback(run, args.prompt || "");
|
|
1266
|
-
const managerOk = managerResult.ok && manager.verdict !== "PARSE_FAILED";
|
|
2021
|
+
const managerBudget = remainingCouncilBudget(run, timeoutMs);
|
|
2022
|
+
const managerStep = await runDebateRole(run, "portfolio_manager", { bull, bear, outputMode }, managerBudget);
|
|
2023
|
+
const managerError = debateFailure(managerStep);
|
|
2024
|
+
const managerOk = !managerError;
|
|
2025
|
+
const manager = managerOk ? managerStep.packet : managerFallback(run, args.prompt || "");
|
|
1267
2026
|
writeJson(join(dir, "manager_synthesis.json"), manager);
|
|
1268
2027
|
writeJson(join(dir, "decision.json"), manager);
|
|
1269
2028
|
updateAgent(run, "portfolio_manager", managerOk ? "completed" : "failed", {
|
|
@@ -1271,9 +2030,7 @@ async function synthesizeQuickDecision(run, args, timeoutMs, outputMode) {
|
|
|
1271
2030
|
output: join(dir, "manager_synthesis.json"),
|
|
1272
2031
|
error: managerOk
|
|
1273
2032
|
? undefined
|
|
1274
|
-
:
|
|
1275
|
-
? "parse_failed"
|
|
1276
|
-
: managerResult.deadline_exhausted ? "global_deadline" : managerResult.timedOut ? "timeout" : `exit code ${managerResult.code ?? "unknown"}`,
|
|
2033
|
+
: managerError,
|
|
1277
2034
|
});
|
|
1278
2035
|
|
|
1279
2036
|
const gate = verificationStatus(run);
|
|
@@ -1340,6 +2097,47 @@ function finalizeBeforeDebate(run, args, reason) {
|
|
|
1340
2097
|
return { bull: null, bear: null, manager, ...finalArtifacts };
|
|
1341
2098
|
}
|
|
1342
2099
|
|
|
2100
|
+
function finalizeAfterDebateFailure(run, args, reason, bullRounds = [], bearRounds = []) {
|
|
2101
|
+
const dir = runPath(run.run_id);
|
|
2102
|
+
const bull = mergeDebateRounds(bullRounds.map((step) => step?.packet).filter(Boolean));
|
|
2103
|
+
const bear = mergeDebateRounds(bearRounds.map((step) => step?.packet).filter(Boolean));
|
|
2104
|
+
if (bull) writeJson(join(dir, "bull_researcher.json"), bull);
|
|
2105
|
+
if (bear) writeJson(join(dir, "bear_researcher.json"), bear);
|
|
2106
|
+
updateAgent(run, "bull_researcher", "failed", {
|
|
2107
|
+
error: reason,
|
|
2108
|
+
completed_at: new Date().toISOString(),
|
|
2109
|
+
output: bull ? join(dir, "bull_researcher.json") : undefined,
|
|
2110
|
+
});
|
|
2111
|
+
updateAgent(run, "bear_researcher", "failed", {
|
|
2112
|
+
error: reason,
|
|
2113
|
+
completed_at: new Date().toISOString(),
|
|
2114
|
+
output: bear ? join(dir, "bear_researcher.json") : undefined,
|
|
2115
|
+
});
|
|
2116
|
+
updateAgent(run, "portfolio_manager", "skipped", {
|
|
2117
|
+
error: reason,
|
|
2118
|
+
completed_at: new Date().toISOString(),
|
|
2119
|
+
});
|
|
2120
|
+
const manager = managerFallback(run, args.prompt || "");
|
|
2121
|
+
writeJson(join(dir, "manager_synthesis.json"), manager);
|
|
2122
|
+
writeJson(join(dir, "decision.json"), manager);
|
|
2123
|
+
run.phase = "incomplete";
|
|
2124
|
+
run.status = "incomplete";
|
|
2125
|
+
run.completed_at = new Date().toISOString();
|
|
2126
|
+
const completeness = completenessStatus(run);
|
|
2127
|
+
appendEvent(run, "incomplete", {
|
|
2128
|
+
reason,
|
|
2129
|
+
downstream_model_calls_skipped: true,
|
|
2130
|
+
missing_evidence: completeness.missing_evidence,
|
|
2131
|
+
missing_debate: completeness.missing_debate,
|
|
2132
|
+
missing_masters: completeness.missing_masters,
|
|
2133
|
+
});
|
|
2134
|
+
const finalArtifacts = writeFinalArtifacts(run, { bull, bear, manager });
|
|
2135
|
+
writeJson(join(dir, "evidence.json"), run);
|
|
2136
|
+
writeStatus(run);
|
|
2137
|
+
writeAllAgentsMarkdown(run, { bull, bear, manager });
|
|
2138
|
+
return { bull, bear, manager, ...finalArtifacts };
|
|
2139
|
+
}
|
|
2140
|
+
|
|
1343
2141
|
export async function synthesizeDecision(run, args) {
|
|
1344
2142
|
const dir = runPath(run.run_id);
|
|
1345
2143
|
const requestedSynthesisMs = Number.isFinite(args.synthesis_timeout_ms)
|
|
@@ -1347,7 +2145,7 @@ export async function synthesizeDecision(run, args) {
|
|
|
1347
2145
|
: Number(args.timeout_ms || LIMITS.CODEX_TIMEOUT_MS);
|
|
1348
2146
|
const timeoutMs = run.council_mode === "quick"
|
|
1349
2147
|
? Math.min(requestedSynthesisMs, LIMITS.QUICK_SYNTHESIS_MS)
|
|
1350
|
-
: requestedSynthesisMs;
|
|
2148
|
+
: Math.min(requestedSynthesisMs, LIMITS.FULL_DEBATE_MS);
|
|
1351
2149
|
const outputMode = OUTPUT_MODES.includes(args.output_mode) ? args.output_mode : "public_equity";
|
|
1352
2150
|
run.phase = "debate";
|
|
1353
2151
|
run.status = "running";
|
|
@@ -1403,28 +2201,66 @@ export async function synthesizeDecision(run, args) {
|
|
|
1403
2201
|
return synthesizeQuickDecision(run, args, timeoutMs, outputMode);
|
|
1404
2202
|
}
|
|
1405
2203
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
2204
|
+
const rejectedStep = (reason, role) => {
|
|
2205
|
+
const result = {
|
|
2206
|
+
ok: false, code: null, text: "", stderr: cleanLog(reason?.message || reason), stdout: "",
|
|
2207
|
+
timedOut: false, unexpected_error: true,
|
|
2208
|
+
};
|
|
2209
|
+
return { result, packet: debateFromCodex(result, role, run, "unexpected debate failure") };
|
|
2210
|
+
};
|
|
2211
|
+
const parallelRound = async (round, bullContext, bearContext) => {
|
|
2212
|
+
const budget = remainingCouncilBudget(run, timeoutMs);
|
|
2213
|
+
appendEvent(run, "debate_round", { round, format: "parallel_per_round", budget_ms: budget });
|
|
2214
|
+
const [bullOutcome, bearOutcome] = await Promise.allSettled([
|
|
2215
|
+
runDebateRole(run, "bull_researcher", { round, ...bullContext }, budget),
|
|
2216
|
+
runDebateRole(run, "bear_researcher", { round, ...bearContext }, budget),
|
|
2217
|
+
]);
|
|
2218
|
+
return {
|
|
2219
|
+
bull: bullOutcome.status === "fulfilled" ? bullOutcome.value : rejectedStep(bullOutcome.reason, "bull_researcher"),
|
|
2220
|
+
bear: bearOutcome.status === "fulfilled" ? bearOutcome.value : rejectedStep(bearOutcome.reason, "bear_researcher"),
|
|
2221
|
+
};
|
|
2222
|
+
};
|
|
2223
|
+
const failedRound = (round) => [debateFailure(round.bull), debateFailure(round.bear)].filter(Boolean);
|
|
2224
|
+
|
|
2225
|
+
// Three-round debate with a strict inter-round barrier and parallel sides per round.
|
|
2226
|
+
const r1 = await parallelRound(1, { brief: "long" }, { brief: "short" });
|
|
2227
|
+
if (failedRound(r1).length) {
|
|
2228
|
+
return finalizeAfterDebateFailure(run, args, `debate_round_1_failed:${failedRound(r1).join(",")}`, [r1.bull], [r1.bear]);
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
const r2 = await parallelRound(2,
|
|
2232
|
+
{ otherCaseR1: r1.bear.packet },
|
|
2233
|
+
{ otherCaseR1: r1.bull.packet });
|
|
2234
|
+
if (failedRound(r2).length) {
|
|
2235
|
+
return finalizeAfterDebateFailure(run, args, `debate_round_2_failed:${failedRound(r2).join(",")}`, [r1.bull, r2.bull], [r1.bear, r2.bear]);
|
|
2236
|
+
}
|
|
2237
|
+
const exactlyThreeQuestions = (packet) => Array.isArray(packet?.questions)
|
|
2238
|
+
&& packet.questions.length === 3
|
|
2239
|
+
&& packet.questions.every((question) => typeof question === "string" && question.trim());
|
|
2240
|
+
if (!exactlyThreeQuestions(r2.bull.packet) || !exactlyThreeQuestions(r2.bear.packet)) {
|
|
2241
|
+
appendEvent(run, "debate_qna_gate", { status: "failed", errors: ["round 2 did not produce exactly three questions per side"] });
|
|
2242
|
+
return finalizeAfterDebateFailure(run, args, "debate_round_2_questions_incomplete", [r1.bull, r2.bull], [r1.bear, r2.bear]);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
const r3 = await parallelRound(3, {
|
|
2246
|
+
otherCaseR1: r2.bear.packet,
|
|
2247
|
+
questionsYouAsked: r2.bull.packet.questions,
|
|
2248
|
+
questionsForYou: r2.bear.packet.questions,
|
|
2249
|
+
}, {
|
|
2250
|
+
otherCaseR1: r2.bull.packet,
|
|
2251
|
+
questionsYouAsked: r2.bear.packet.questions,
|
|
2252
|
+
questionsForYou: r2.bull.packet.questions,
|
|
2253
|
+
});
|
|
2254
|
+
if (failedRound(r3).length) {
|
|
2255
|
+
return finalizeAfterDebateFailure(run, args, `debate_round_3_failed:${failedRound(r3).join(",")}`, [r1.bull, r2.bull, r3.bull], [r1.bear, r2.bear, r3.bear]);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
const bullR1 = r1.bull;
|
|
2259
|
+
const bearR1 = r1.bear;
|
|
2260
|
+
const bullR2 = r2.bull;
|
|
2261
|
+
const bearR2 = r2.bear;
|
|
2262
|
+
const bullR3 = r3.bull;
|
|
2263
|
+
const bearR3 = r3.bear;
|
|
1428
2264
|
|
|
1429
2265
|
const bull = mergeDebateRounds([bullR1.packet, bullR2.packet, bullR3.packet]);
|
|
1430
2266
|
const bear = mergeDebateRounds([bearR1.packet, bearR2.packet, bearR3.packet]);
|
|
@@ -1435,6 +2271,9 @@ export async function synthesizeDecision(run, args) {
|
|
|
1435
2271
|
bearR3: bearR3.packet,
|
|
1436
2272
|
});
|
|
1437
2273
|
appendEvent(run, "debate_qna_gate", qnaGate);
|
|
2274
|
+
if (qnaGate.status !== "passed") {
|
|
2275
|
+
return finalizeAfterDebateFailure(run, args, "debate_round_3_qna_incomplete", [bullR1, bullR2, bullR3], [bearR1, bearR2, bearR3]);
|
|
2276
|
+
}
|
|
1438
2277
|
const bullQnaErrors = qnaGate.errors.filter((error) => error.startsWith("bull_researcher"));
|
|
1439
2278
|
const bearQnaErrors = qnaGate.errors.filter((error) => error.startsWith("bear_researcher"));
|
|
1440
2279
|
const bullTransportOk = [bullR1, bullR2, bullR3].every((step) => step.result.ok);
|
|
@@ -1472,24 +2311,17 @@ export async function synthesizeDecision(run, args) {
|
|
|
1472
2311
|
});
|
|
1473
2312
|
writeAllAgentsMarkdown(run, { bull, bear });
|
|
1474
2313
|
|
|
1475
|
-
const
|
|
1476
|
-
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
}, ({ pid, output, elapsed_ms }) => {
|
|
1480
|
-
updateAgent(run, "portfolio_manager", "running", { pid, output });
|
|
1481
|
-
appendEvent(run, "agent_heartbeat", { role: "portfolio_manager", pid, output, elapsed_ms });
|
|
1482
|
-
});
|
|
1483
|
-
const manager = managerResult.ok
|
|
1484
|
-
? debateFromCodex(managerResult, "portfolio_manager", run, managerPrompt)
|
|
1485
|
-
: managerFallback(run, args.prompt || "");
|
|
2314
|
+
const pmBudget = remainingCouncilBudget(run, Math.min(requestedSynthesisMs, LIMITS.FULL_PM_MS));
|
|
2315
|
+
const managerStep = await runDebateRole(run, "portfolio_manager", { bull, bear, outputMode }, pmBudget);
|
|
2316
|
+
const managerOk = !debateFailure(managerStep);
|
|
2317
|
+
const manager = managerOk ? managerStep.packet : managerFallback(run, args.prompt || "");
|
|
1486
2318
|
const gate = verificationStatus(run);
|
|
1487
2319
|
writeJson(join(dir, "manager_synthesis.json"), manager);
|
|
1488
2320
|
writeJson(join(dir, "decision.json"), manager);
|
|
1489
|
-
updateAgent(run, "portfolio_manager",
|
|
2321
|
+
updateAgent(run, "portfolio_manager", managerOk ? "completed" : "failed", {
|
|
1490
2322
|
completed_at: new Date().toISOString(),
|
|
1491
2323
|
output: join(dir, "manager_synthesis.json"),
|
|
1492
|
-
error:
|
|
2324
|
+
error: managerOk ? undefined : debateFailure(managerStep),
|
|
1493
2325
|
});
|
|
1494
2326
|
const completeness = completenessStatus(run);
|
|
1495
2327
|
run.completed_at = new Date().toISOString();
|
|
@@ -1532,7 +2364,7 @@ export async function analyzeSymbol(args) {
|
|
|
1532
2364
|
artifacts: debate.artifacts || artifactPaths(run),
|
|
1533
2365
|
};
|
|
1534
2366
|
}
|
|
1535
|
-
if (
|
|
2367
|
+
if (remainingCouncilBudget(run, 1) <= 0) {
|
|
1536
2368
|
const debate = finalizeBeforeDebate(run, args, "global_deadline_before_masters");
|
|
1537
2369
|
return {
|
|
1538
2370
|
run,
|
|
@@ -1546,8 +2378,7 @@ export async function analyzeSymbol(args) {
|
|
|
1546
2378
|
}
|
|
1547
2379
|
await runHeadlessMasters(run, args);
|
|
1548
2380
|
gate = completenessStatus(run);
|
|
1549
|
-
if (gate.missing_masters.length > 0
|
|
1550
|
-
|| (run.council_mode === "quick" && remainingQuickBudget(run, 1) <= 0)) {
|
|
2381
|
+
if (gate.missing_masters.length > 0 || remainingCouncilBudget(run, 1) <= 0) {
|
|
1551
2382
|
const debate = finalizeBeforeDebate(run, args,
|
|
1552
2383
|
gate.missing_masters.length ? "master_gate_failed" : "global_deadline_before_debate");
|
|
1553
2384
|
return {
|