MINARA

Backtesting Feedback Loop

Backtesting Feedback Loop (Sprint 6 — online outcome filler)

Periodic job that computes P&L outcomes for executed trades N hours old and feeds them back into EvaluationLoop + MethodologyStore so the Wilson-LB graduation machinery accrues real win/loss signal. Entirely dark by default (BACKTEST_ENABLED=false). Does not replay historical trades or re-run past decisions — only evaluates rows that actually executed in-session. Rollout protocol: 1. BACKTEST_ENABLED=true + BACKTEST_DRY_RUN=true for 1 week 2. Inspect shadow_runs WHERE facet='backtest_outcome' 3. Flip BACKTEST_DRY_RUN=false when outcomes look clean 4. Flip LEARNING_RECORD_USAGE=true last — enables the Wilson counter updates

BACKTEST_ENABLED

master switch for the runner + scheduler. false (default) = runner is never constructed and the cron timer is not registered. Zero runtime cost. true = runner is constructed, scheduler fires every BACKTEST_CRON_HOURS hours. Still respects DRY_RUN below.

  • Format: true / false.
  • Setting home: Settings → Preferences (schema key)

BACKTEST_DRY_RUN

when true, runner computes outcomes and emits to shadow_runs(facet='backtest_outcome') but DOES NOT call updateTradeOutcome or recordUsage. Flip to false only after spot-checking shadow rows for at least one full cron cycle.

  • Format: true / false.
  • Setting home: Not a user-facing setting

BACKTEST_MIN_TRADE_AGE_MS

minimum age (ms) before a trade becomes eligible for backtest evaluation. Outcomes need time to develop; a 5-minute horizon is noise. Passed through to ReviewEngine's minTradeAgeForEvalMs. Default 86400000 (24h).

  • Format: positive integer (ms).
  • Setting home: Not a user-facing setting

BACKTEST_OUTCOME_HORIZON_HOURS

hours after trade created_at at which the outcome price is sampled. +5% in 24h = 24. Mirrored in the emitted outcome string so the evaluator sees the window length. Single global for Sprint 6 (TODOS #9 tracks per-trade-type tuning). Default 24.

  • Format: positive integer (hours).
  • Setting home: Not a user-facing setting

BACKTEST_BATCH_LIMIT

max pending rows the runner pulls per fire. Passed through to ReviewEngine's maxEvalsPerBatch. Lower to cap per-run LLM spend; raise when the pending queue stays warm. Default 20.

  • Format: positive integer.
  • Setting home: Not a user-facing setting

BACKTEST_CRON_HOURS

scheduler interval. The runner is invoked via setInterval(… * 3600_000); the timer uses unref() so it never keeps the process alive on its own. Floating point allowed; values below 0.017 (1 minute) are clamped up. Default 24.

  • Format: positive number (hours).
  • Setting home: Not a user-facing setting

BACKTEST_PRICE_PROVIDER

force a single historical price source for debugging. auto routes by asset class (crypto → Hyperliquid → Yahoo -USD fallback; stock/unknown → Yahoo; stablecoin → 1.0). Pin to hyperliquid when suspecting Yahoo symbol normalization; pin to yahoo when Hyperliquid is rate-limited.

  • Format: auto / hyperliquid / yahoo.
  • Setting home: Not a user-facing setting

BACKTEST_MAX_COST_USD_PER_RUN

per-invocation hard cost cap. Runner snapshots BudgetTracker.getDailySpend("learning") before and after evaluatePacked; if the delta exceeds this, the run returns status=stopped_budget. 0 disables the cap. Default 2.00.

  • Stackable: existing daily/monthly caps in BudgetTracker still apply.
  • Format: non-negative float (USD).
  • Setting home: Not a user-facing setting

LEARNING_RECORD_USAGE

when true, EvaluationLoop checks trade_history.methodology_ids after each evaluated trade. Phase 1.2a dedup (codex R1 P1) intentionally suppresses the recordUsage call for trades that have ANY attributed methodology — those trades are Wilson-trained by case-attribution.ts instead. Trades without attribution today produce no Wilson signal from this path either; Wilson updates flow exclusively through case-attribution.

  • This env var is therefore an observability gate now, not a learning
  • gate: flipping it to true triggers a wilson_dedup_skip log line
  • per attributed trade so operators can verify the dedup is firing in
  • production. The legacy "blanket recordUsage on every evaluated trade"
  • behaviour is gone.
  • Format: true / false.
  • Setting home: Settings → Preferences (schema key)

LEARNING_TUNING_ENABLED

master gate for the offline Bayesian- optimization tuning harness that searches for better values of LEARNING_CONFIG in src/learning/methodology-store.ts. The harness is an offline tool (runs via minara learning replay + Python BO in tools/tuning/), NEVER part of the request path. Gate exists to prevent accidental invocation of the replay CLI in prod.

  • Consumed by: src/gateway/learning-cli.ts — the replay subcommand
  • short-circuits with { skipped: "tuning_disabled" } when this is
  • not true.
  • Exception: minara learning stats is pure readonly (SQL SELECT on
  • trade_history + methodologies for data-readiness reporting) and
  • runs regardless of this gate. Ops need continuous visibility into
  • whether enough Sprint 6 data has accumulated to justify a tuning
  • run (target: ≥100 evaluated trades covering ≥20 unique methodology
  • ids).
  • Rollout: this stays false until (a) Sprint 6 rollout is complete
  • (LEARNING_RECORD_USAGE=true), (b) minara learning stats reports
  • READY_FOR_BO=true, and (c) a human operator explicitly starts a
  • tuning session. Leaving it false in prod is the default-safe
  • posture.
  • Format: true / false.
  • Setting home: Not a user-facing setting

Phase 1 — Decision Capture (advice BUY/SELL/HOLD)

The decision-capture hook runs at turn-end in the agent loop. When the turn involves investment advice (one of 9 advice scenarios active, OR a BUY/SELL/HOLD keyword + ticker match in the agent's response), an independent summarizer LLM call extracts a structured {asset, decision, confidence, quoted_price} tuple and persists it to decision_history. This data feeds Phase 2 multi-horizon backtest and Phase 6 methodology-instance BO tuning. The summarizer is ALWAYS fire-and-forget (async); user-visible turn latency is unchanged. Failures log + drop the row silently.

DECISION_CAPTURE_ENABLED

master switch for Phase 1 capture. When false (default), the turn-end hook returns immediately without running any pre-filter or LLM call. Flip to true to start populating decision_history. Safe to flip any time — no data migration required.

  • Consumed by: src/learning/decision-capture/capture-hook.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Settings → Preferences (schema key)

DECISION_SUMMARIZER_MODEL

model for the decision summarizer LLM call. Default targets Claude Haiku 4.5 for cost efficiency (~$0.002 per turn at 800 input + 200 output tokens). Can be bumped to Sonnet if summarizer coverage rate < 70% in stats.

  • Consumed by: src/learning/decision-capture/summarizer.ts.
  • When unset: defaults to claude-haiku-4-5-20251001.
  • Format: Anthropic model id.
  • Setting home: Not a user-facing setting

DECISION_SUMMARIZER_TIMEOUT_MS

hard timeout for a single summarizer call. Summarizer runs fire-and-forget; timeout drops the decision with a warn log, no retry.

  • Consumed by: src/learning/decision-capture/summarizer.ts.
  • When unset: defaults to 15000.
  • Format: positive integer (ms).
  • Setting home: Not a user-facing setting

DECISION_CAPTURE_SYNC_MODE

when true, awaits summarizer before returning from the hook (adds turn latency). Only for deterministic tests / CI; prod should stay false.

  • Consumed by: src/learning/decision-capture/capture-hook.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Not a user-facing setting

DECISION_CAPTURE_HEURISTIC_ENABLED

Tier 2 regex pre-filter. When true (default), the hook also captures turns where the agent's final response contains a BUY/SELL/HOLD keyword + asset ticker even if no advice scenario was active. Set to false to limit capture strictly to advice-scenario turns.

  • Consumed by: src/learning/decision-capture/classify-capture-source.ts.
  • When unset: defaults to true (enabled).
  • Format: true / false.
  • Setting home: Not a user-facing setting

DECISION_CAPTURE_UNIVERSAL_SCAN

Tier 3 opt-in universal scan. When true, every turn invokes the summarizer (bypass Tier 1/2). Only for diagnostic A/B runs — permanent use would ~4x the summarizer budget. Leaving this false is strongly recommended.

  • Consumed by: src/learning/decision-capture/classify-capture-source.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Not a user-facing setting

Phase 2 — Multi-Horizon Decision Backtest

DecisionBacktestRunner scans decision_history rows whose age ≥ max_horizon (default 30 days for the 1m horizon) and fills: (a) real_price_at_decision from HistoricalPriceProvider, compared against agent_quoted_price for hallucination flag (b) decision_outcomes rows — one per horizon {1d, 3d, 1w, 1m} If |agent_quoted - real|/real > HALLUCINATION_MAX_PRICE_DELTA_PCT, the decision is marked state='skipped_halluc' and excluded from downstream learning.

DECISION_BACKTEST_ENABLED

master switch for the decision-history multi-horizon backtest cron. EXPERIMENTAL in the current release: the runner now wires a ReasoningQualityJudge (see src/learning/backtest/reasoning-quality.ts) so that "agent said HOLD but market went up" misses are NOT used to update Wilson directly — that pattern would train the agent to chase the previous market move (i.e. retail-investor behaviour the product team explicitly wants to avoid). Phase 1 ships the no-op default judge that classifies every decision as no_judgment, which means flipping this flag to true today only fills decision_outcomes rows + emits verdict tallies; no methodology Wilson counter is incremented from this path. Wait for the Phase 2 EvaluationProvider to land a real LLM-as-judge before relying on the cron for learning. Until then: - false (default): runner is a no-op. - true (dry-run-only recommended): fills decision_outcomes, counts verdicts in run summary, never updates Wilson.

  • Consumed by: src/learning/backtest/decision-runner.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Settings → Preferences (schema key)

METHODOLOGY_LEARNING_CRON_ENABLED

opt-in for the in-process methodology cron orchestrator (sweep → 7d case-attribution → synthesis). When unset (default), the agent does NOT auto-schedule; operators advance the loop via minara learning cron from system cron / launchctl / systemd. When set to true, the orchestrator runs every METHODOLOGY_LEARNING_CRON_INTERVAL_MS (default 6h). Wilson training for executed trades flows through case-attribution under this gate — flip it on only after the install accumulates ≥ 100 real cases per active asset_class so the signal is not noisy.

  • Consumed by: src/app.ts + src/learning/methodology-cron.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Settings → Preferences (schema key)

METHODOLOGY_LEARNING_CRON_INTERVAL_MS

interval in milliseconds between cron passes when METHODOLOGY_LEARNING_CRON_ENABLED=true. Defaults to 21600000 (6 hours). Smaller intervals burn LLM judge budget faster without producing more signal.

  • Consumed by: src/learning/methodology-cron.ts.
  • When unset: defaults to 21600000.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_ENABLED

opt-in for the experimental methodology tuning (BO planner) sub-tree under apps/agent/src/learning/experimental/ tuning/. Phase 1.7 moved this code out of the default agent boot path because cycle-planner.ts is a stub that emits a dry-run plan only — it never executes BO and therefore never mutates any methodology. Flip to true only when you want to inspect the planner's tunability-score output via minara learning tune-methodology --dry-run; the live executor lands in a future phase.

  • Consumed by: src/gateway/learning-cli.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Settings → Preferences (schema key)

DECISION_REPLAY_ENABLED

opt-in for the experimental decision-replay sub-tree under apps/agent/src/learning/experimental/replay/. The current decision-replay.ts implementation is explicitly a PLACEHOLDER (see the file header) — it returns a simple weighted- average reward and does NOT do threshold-aware replay. Do not rely on its output to support methodology adjustments. Phase 6+ work replaces the placeholder with a real engine.

  • Consumed by: src/gateway/learning-cli.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Settings → Preferences (schema key)

DECISION_BACKTEST_DRY_RUN

when true, runner does NOT write to decision_outcomes / decision_history; instead logs outcome + flags to shadow_runs(facet='decision_outcome'). Week-1 rollout protocol. Flip to false after verifying shadow rows look sane.

  • Consumed by: src/learning/backtest/decision-runner.ts.
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Not a user-facing setting

DECISION_BACKTEST_HORIZONS

CSV of horizon specs in the form <number><unit> where unit is h/d/w/m (hours/days/weeks/months). e.g. 1d,3d,1w,1m. Each horizon produces one row in decision_outcomes per decision. Max horizon determines when a pending decision becomes eligible for backtest.

  • Consumed by: src/learning/backtest/decision-runner.ts.
  • When unset: defaults to 1d,3d,1w,1m.
  • Format: comma-separated list.
  • Setting home: Not a user-facing setting

DECISION_BACKTEST_CRON_HOURS

interval between runner invocations.

  • Consumed by: src/app/backtest.ts (wireBacktest interval).
  • When unset: defaults to 24.
  • Format: positive number (hours).
  • Setting home: Not a user-facing setting

DECISION_BACKTEST_MAX_AGE_DAYS

hard ceiling on decision age. Rows older than this are skipped regardless of horizon fill state, to prevent unbounded backlog growth.

  • Consumed by: src/learning/backtest/decision-runner.ts.
  • When unset: defaults to 60.
  • Format: positive integer (days).
  • Setting home: Not a user-facing setting

HALLUCINATION_MAX_PRICE_DELTA_PCT

when agent_quoted_price is set and deviates from real_price_at_decision by more than this ratio, the decision is flagged and excluded from downstream learning. 0.05 (5%) defeats most tool-output staleness false positives while still catching large fabrications. Tighten to 0.03 if stale caches common; loosen to 0.10 only if HistoricalPriceProvider itself is noisy.

  • Consumed by: src/learning/backtest/decision-outcome-filler.ts.
  • When unset: defaults to 0.05.
  • Format: positive decimal (0.01 = 1%).
  • Setting home: Not a user-facing setting

Phase 3 — Reward Computation

Reward function converts the 4-horizon return vector (Phase 2) into a single scalar per decision. Three rules: BUY: weighted mean of pct_return (reward rises with price up) SELL: weighted mean of -pct_return (reward rises with price down) HOLD: linear neutrality reward — |return| < threshold gives positive reward (1 at 0, decaying to 0 at threshold); beyond threshold, linear negative (opportunity cost / missed move).

DECISION_HORIZON_WEIGHTS_JSON

per-horizon weights in reward sum. Stringified JSON object {label: weight}. Missing labels get 0 weight. Default reflects "medium-term signal" bias — 1w highest, 1d lowest since daily noise.

  • Consumed by: src/learning/decision-reward/reward.ts rewardOptionsFromEnv().
  • When unset: defaults to {"1d":0.15,"3d":0.25,"1w":0.35,"1m":0.25}.
  • Format: JSON string.
  • Example (more emphasis on short-term): DECISION_HORIZON_WEIGHTS_JSON='{"1d":0.35,"3d":0.30,"1w":0.25,"1m":0.10}'
  • Setting home: Not a user-facing setting

DECISION_HOLD_NEUTRALITY_THRESHOLD

|pct_return| below this is counted as a HOLD win. 0.02 = 2%. Raise to loosen the HOLD reward (more "volatile is fine" tolerance); lower to tighten (HOLD must be near-zero motion).

  • Consumed by: src/learning/decision-reward/reward.ts rewardOptionsFromEnv().
  • When unset: defaults to 0.02.
  • Format: positive decimal (0.01 = 1%).
  • Setting home: Not a user-facing setting

Phase 6 — BO Tuning Cycle

Offline Bayesian optimization over per-(template, asset_class) methodology thresholds. Runs periodically (cron), reads decision rewards from Phase 1-3, writes tuned thresholds into methodology_instances. Fully dark-landed — zero runtime cost until enabled. Requires Python harness in tools/tuning/.

METHODOLOGY_INSTANCE_TUNING_ENABLED

master switch for the BO cycle orchestrator. When false (default), the cycle is a no-op even if scheduled. Flip to true only after Phase 1/2/3 data has accumulated AND minara learning stats READY_FOR_BO=true.

  • Consumed by: src/learning/experimental/tuning/cycle.ts (to be wired
  • in Phase 7).
  • When unset: defaults to false.
  • Format: true / false.
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_CRON_DAYS

days between BO cycle invocations. Defaults to 7 (weekly). Lower values risk overfitting to short-term market noise; higher values slow learning.

  • Consumed by: src/app.ts (cron wiring, Phase 7).
  • When unset: defaults to 7.
  • Format: positive integer (days).
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_MAX_BUCKETS_PER_CYCLE

per-cycle cap. Eligible buckets are ranked by tunability_score; only top-N are passed to the Python BO harness each cycle.

  • Consumed by: src/learning/experimental/tuning/cycle.ts.
  • When unset: defaults to 10.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_PROFILES_PATH

override path for asset-class profiles JSON. Per-class shallow merge over BUILTIN_PROFILES. Set value null for a class to exclude it from tuning entirely.

  • Consumed by: src/learning/experimental/tuning/asset-profiles.ts.
  • When unset: defaults to $MINARA_DATA_DIR/methodology-tuning-profiles.json.
  • Format: filesystem path (may not exist — no-op in that case).
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_MIN_DECISIONS_GLOBAL

global floor for min_decisions across ALL profiles (takes max with per-profile values). Use for emergency tightening, e.g. after a bad rollout.

  • Consumed by: src/learning/experimental/tuning/asset-profiles.ts.
  • When unset: no floor applied.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_MIN_IMPROVEMENT_REL

Post-BO check #1 relative improvement gate (test-split mean reward must exceed baseline by this ratio). 0.05 = 5%.

  • Consumed by: src/learning/experimental/tuning/cycle.ts Phase 7.
  • When unset: defaults to 0.05.
  • Format: positive decimal.
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_MAX_SENSITIVITY_DROP_10PCT

Post-BO check #2 narrow-peak rejection. The best candidate's ±10%-neighborhood score must not drop by more than this fraction. 0.5 = 50%.

  • Consumed by: src/learning/experimental/tuning/cycle.ts Phase 7.
  • When unset: defaults to 0.5.
  • Format: decimal in (0, 1].
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_PARAM_BOUND_REL

BO pbounds half-width as a multiple of template default. 0.5 means bounds = [default × 0.5, default × 1.5] for each parameter.

  • Consumed by: src/learning/experimental/tuning/cycle.ts Phase 7.
  • When unset: defaults to 0.5.
  • Format: positive decimal.
  • Setting home: Not a user-facing setting

METHODOLOGY_TUNING_MIN_CAPTURE_CONFIDENCE

BO replay harness only considers decisions with capture_confidence ≥ this threshold. 0.3 includes all three tiers (advice_scenario, heuristic_match, summarizer_promoted); raise to 0.6+ to restrict to high-signal decisions only.

  • Consumed by: src/learning/experimental/replay/decision-replay.ts (Phase 7).
  • When unset: defaults to 0.3.
  • Format: decimal in [0, 1].
  • Setting home: Not a user-facing setting

Preference Evolution (M2: Financial Auto-Memory)

Periodic LLM-driven proposer that scans recent user messages, asks the model to cluster them into durable preferences (kind ∈ {personal_style, behavioral_preference}), and surfaces card-style graduation asks once per cooldown window. Modeled on AutoClaw's weekly evolution loop with a 1-3 graduations/week soft cap. M2 boundary: hard_constraint candidates are demoted to behavioral_preference and never auto-applied at the tool level — tool-level enforcement lands in M3 alongside the keyword scanner. Manual /preferences approve|reject|deprecate works regardless of the master flag for operator overrides.

PREFERENCE_LEARNING

master switch for M2 periodic proposer + graduation card flow. When 0, the agent loop does NOT queue user messages, does NOT fire the proposer, does NOT inject the graduation card. The PreferenceStore + REPL/CLI/REST endpoints from M1 remain available for manual management even when this flag is off.

  • Format: 0 / 1.
  • Setting home: Settings → Preferences (schema key)

PREFERENCE_PROPOSER_INTERVAL

turns between consecutive proposer fires. The proposer runs as a fire-and-forget async after the turn's user-visible response is sent, so this is amortized cost, not user latency. Lower = more responsive learning + higher LLM spend; default 30 ≈ once per ~half-hour of active conversation.

  • Format: positive integer.
  • Setting home: Not a user-facing setting

PREFERENCE_WEEKLY_QUOTA

max graduations allowed inside any rolling 7-day window. Once hit, the proposer skips its cycle — new candidates back up in the queue but are not surfaced to the user. Manual /preferences approve overrides the quota (operator choice). Mirrors AutoClaw's "1-3 deep evolutions per week" principle.

  • Format: positive integer.
  • Setting home: Not a user-facing setting

PREFERENCE_DEDUP_THRESHOLD

TF-IDF cosine score above which a candidate is treated as a duplicate of an existing live preference (state ∈ {active, proposed, deprecated}) and dropped before persisting. Range [0, 1]; 0.85 catches paraphrases without false-positives on short statements with shared keywords.

  • Format: float in [0, 1].
  • Setting home: Not a user-facing setting

PREFERENCE_PROPOSER_BATCH_SIZE

max recent candidates pulled into a single proposer LLM call. Larger batches give the LLM more clustering signal but cost more tokens; default 200 is enough for a meaningful proposer cycle without pinning the rest of the context.

  • Format: positive integer.
  • Setting home: Not a user-facing setting

PREFERENCE_MIN_CLUSTER_SIZE

minimum number of candidate messages the proposer's LLM must report as supporting a single cluster before the proposal is persisted. Floor of 3 prevents singleton observations from inflating preference noise.

  • Format: positive integer (≥ 2 enforced).
  • Setting home: Not a user-facing setting

PREFERENCE_ASK_COOLDOWN_HOURS

minimum hours between consecutive graduation asks for the SAME preference. After the user replies "later" (or doesn't reply at all), the row stays proposed but is hidden from the ask queue until this window passes. Default 24h matches the scenario flow.

  • Format: positive integer.
  • Setting home: Not a user-facing setting

PREFERENCE_ASK_MIN_GAP_TURNS

minimum turns between consecutive graduation asks (across DIFFERENT preferences) within the same REPL session. Prevents back-to-back card asks even when the proposer queue is rich.

  • Format: positive integer.
  • Setting home: Not a user-facing setting

PREFERENCE_SKIP_IN_CHAT_ASK

disable the in-chat graduation card entirely. When 1, the proposer still runs and writes proposals, but the card is never injected into the system prompt; operators review and approve via REPL /preferences pending + approve, or via CLI / REST. Useful in non-interactive deployments.

  • Format: 0 / 1.
  • Setting home: Not a user-facing setting

M3: keyword scanner + tool-level constraint enforcement

PREFERENCE_STYLE_MIN_OBSERVATIONS

independent observations of the SAME style preference (by dedup_key) the scanner needs before it auto-activates. Floor 1; raising to 3+ gives the user more chances to contradict themselves before the row sticks.

  • Format: positive integer. Default 2.
  • Setting home: Not a user-facing setting

PREFERENCE_HARD_UNDO_WINDOW_HOURS

how long after a strong-signal auto-activation can the user still run /preferences undo <id> to back out. Rows outside this window must be retired via /preferences deprecate <id> instead.

  • Format: positive integer. Default 24.
  • Setting home: Not a user-facing setting

MINARA_SKIP_FUND_CONFIRM

fund-moving confirmation bypass.

  • What it controls: the unified confirm gate (controlPolicy.confirm on the ToolEntry, interpreted by tools/_security/tier-gate.ts). Fund-moving tools (swap / buy / sell / transfer / perps open-close-cancel / perp-wallet sweep+transfer / autopilot enable / workflow activate / strategy-studio start+deploy) preview, then execute only after the user accepts the confirm card (or other confirm evidence). Handlers stay execute-only.
  • Consumed by: src/tools/_security/tier-gate.ts (confirmFlowHook), via src/tools/_shared/confirm.ts (isFundConfirmBypassEnvSet).
  • When to set: ONLY in non-interactive contexts where there is no human in the loop and the caller is non-human: - backtest runs - workflow engine executions (server-side autopilot, DCA) - CI smoke tests that intentionally hit fund-moving paths Setting this bypasses the confirm gate PROCESS-WIDE; there is no other escape hatch. The LLM cannot flip it; the REPL cannot flip it.
  • When unset: every fund-moving call goes through the confirm gate.
  • Format: 1 / true / yes / on to enable. Anything else = off.
  • ⚠ Never set in an interactive REPL session or on a production deployment unless you are certain every caller is non-human.
  • Setting home: Settings → Preferences (schema key)

WORKBENCH_REMOTE_WEB_PTY_ENABLED

expose the interactive workbench shell through a remote Gateway.

  • What it controls: allows authenticated web clients to create interactive PTY sessions when the Gateway is bound beyond loopback.
  • Consumed by: apps/agent/src/gateway/api.ts and the Settings safety preference safety.remoteWebPty.
  • When to set: only on a trusted, authenticated deployment whose host has a supported OS sandbox. The web UI can also enable it after an explicit critical-risk confirmation.
  • When unset: remote Web PTY stays disabled; loopback Gateways keep their existing local-terminal behavior.
  • Format: 1 / true / yes / on to enable. The workspace boundary, environment allowlist, ownership checks, concurrency cap, and process lifetime limit still apply.
  • Setting home: Settings → Preferences (schema key)

WORKBENCH_REMOTE_SHELL_PROFILE_POLICY

choose how remote Workbench terminals load user shell configuration.

  • What it controls: whether a remote terminal asks before loading the user's shell rc file, loads it automatically, or always uses the built-in Minara shell configuration.
  • Consumed by: apps/agent/src/gateway/api.ts and the internal Workbench terminal capability and creation routes.
  • When to set: set user only on trusted remote hosts where user rc files are expected to run. Set builtin to prohibit loading them. Use prompt to let each browser remember an explicit per-tab choice.
  • When unset: remote Gateways default to prompt. Loopback Gateways always load the user shell configuration and do not prompt.
  • Format: prompt, user, or builtin. Invalid values fall back to prompt and emit one warning.
  • Setting home: Not a user-facing setting

MINARA_DANGEROUSLY_SKIP_PERMISSIONS

⚠ DANGEROUS BUTTON — master skip-non-fund-authorization switch (the analogue of Claude Code's --dangerously-skip-permissions).

  • What it controls: turns off the NON-FUND interactive authorization gates at once, so an agent run never stops to ask for them: 1. Tier gate (tools/_security/tier-gate.ts) — the first-use / high-risk / autonomous-grant prompts for terminal, write_file, execute_code, etc. 2. Command-guard (tools/_security/command-guard.ts) — the shell command tripwire. 3. Sandbox write-escape gate (tools/_security/sandbox-gate.ts). 4. Script-risk YELLOW confirm (tools/_security/script-risk-gate.ts).
  • What it does NOT control: the fund-moving confirm (tools/_shared/confirm.ts) — swap / buy / sell / transfer / perps still require confirm: true while this is set. Money moves are gated independently by MINARA_SKIP_FUND_CONFIRM. In web-ui terms, this switch alone is "Auto" mode (actions run automatically, money moves still confirm); setting both is "Full auto".
  • Consumed by: src/tools/_security/permission-skip.ts (isPermissionSkipSet), read by the tier gate, command-guard, the sandbox gate, and the script-risk gate.
  • Two safety floors stay intact even when set: - Script-risk RED is still a hard reject (RED never prompts, so it is not an "authorization" surface). - A workflow test-run (ctx.test_run) still never moves money.
  • When to set: automated tests / headless drives where there is NO human in the loop and every caller is trusted. Three ways to set: - this env var, or - the --dangerously-skip-permissions CLI flag, or - a persisted safety.skipPermissions override in ~/.minara/runtime-preferences.json (also editable in the web UI Settings danger zone, behind minara settings unlock-critical).
  • When unset: every gate is active and prompts / confirms as normal.
  • Format: 1 / true / yes / on to enable. Anything else = off.
  • ⚠ Never set in an interactive REPL you don't fully control, or on a production deployment. The LLM cannot flip it.
  • Setting home: Settings → Preferences (schema key)

MINARA_AUTO_REVIEW

Smart mode: a reviewer agent decides non-fund approvals that would otherwise prompt the user (Codex AutoReview).

  • What it controls: routes the NON-FUND "would have asked" sites to a short Guardian LLM session instead of the InteractionQueue: 1. Tier gate first-use / high-risk prompts (non-fund). 2. Sandbox write-escape and command-escape prompts. 3. Script-risk YELLOW confirm. Deny is a tool error back to the main agent — no confirm card. Timeout / parse failure fail closed (deny).
  • What it does NOT control: fund-moving confirms stay on the human path. Auto / Full (safety.skipPermissions) skip the ask entirely and take precedence. Coding sessions and automations do not use Smart.
  • Consumed by: src/tools/_security/permission-skip.ts (isGuardianReviewEnabled) and src/guardian/.
  • When to set: when you want the chat menu's Smart / "Approve for me" mode as the process default. The web UI also persists safety.autoReview in runtime-preferences.json.
  • When unset: Ask mode: the user confirms non-fund actions.
  • Format: 1 / true / yes / on to enable. Anything else = off.
  • Setting home: Settings → Preferences (schema key)

DISABLE_SCRIPT_RISK_GATE

⚠ DANGEROUS BUTTON — script-risk kill switch.

  • What it controls: the static-analysis script risk gate that runs before execute_code (python/node body), terminal (shell command), write_file (final file content), and patch (post- apply content). Default behavior classifies the body as RED (auto-reject), YELLOW (AskUserQuestion confirm), or GREEN (allow). RED catches mass rm * / rm -r *, deletion of paths outside the workspace, IMDS / SSRF, container escape, credential / wallet- store reads, indirect-obfuscation + sink combos, remote pickle/yaml deserialization, etc. YELLOW catches fund-moving CLI shell-outs (minara swap / cast send / forge --broadcast), on-chain dangerous calls (approve / Permit2 / Safe owner change), env-poisoning (NODE_OPTIONS / LD_PRELOAD / BASH_ENV), specific- path rm, package installs from git / tarball / non-official index, heredoc-embedded scripts, and process substitution.
  • Consumed by: src/tools/_shared/script-risk-gate.ts.
  • When to set: ONLY for incident response or a fully offline CI run where there is no human to answer AskUserQuestion AND every caller is trusted. This is a single boolean — it bypasses BOTH RED and YELLOW checks. For day-to-day workflow exemptions use the per-workflow script_risk_policy field (body_sha256 + category pre-approval at workflow_activate time); never set this env to widen a single workflow's surface.
  • When unset: gate is active. RED hard-rejects, YELLOW prompts via AskUserQuestion. Cron / workflow contexts with no interactive session return script_risk_no_session unless they have a matching script_risk_policy.
  • Format: 1 / true / yes / on to enable. Anything else = off.
  • Audit: every gate decision lands in the script_risk_decisions SQLite table — set this env and the row's bypassed_by column reads env_global so an operator can later spot the bypass.
  • ⚠ Never set this in interactive REPL sessions or production deployments unless you have verified every caller is non-human and accept that mass-delete / credential-exfil patterns will execute without prompt.
  • Setting home: Settings → Preferences (schema key)

DISABLE_OUTPUT_REDACTION

⚠ DANGEROUS BUTTON — output-redaction kill switch.

  • What it controls: the secret-redaction pass over subprocess output. execute_code / terminal stdout+stderr (local and docker) are scrubbed for credential-shaped strings before they enter the model context and persisted chat history: vendor token shapes (sk-…, ghp_…, xoxb-…, AKIA…, full JWTs) anywhere, plus values bound to secret-ish names (KEY=…, "api_key": "…", Authorization: headers, ://user:password@host URLs). Matches are replaced with [REDACTED].
  • Consumed by: src/tools/_security/redact.ts (applied in src/tools/_shared/subprocess-result.ts and the docker environment).
  • When to set: ONLY while debugging a false positive (a legitimate output the redactor mangles) in a session that handles no real credentials. The switch is read ONCE at process start — an export inside a terminal tool call cannot flip it mid-session; changing it requires a host restart.
  • When unset: redaction is active on every subprocess output path.
  • Format: 1 / true / yes / on to disable. Anything else = on.
  • Setting home: Not a user-facing setting

MINARA_TOOL_RESULT_RETAIN_HOURS

spill file retention window.

  • What it controls: how many hours an oversized tool result persisted under <dataDir>/sandbox/files/.tool-results/ survives before the periodic sweep (every 6h) deletes it. The same value drives both the boot-time sweep and the long-running sweep, so a process that stays up for days never accumulates beyond the configured window.
  • Consumed by: src/core/tool-result-retain.ts, wired into the sweepStaleSpillFiles boot + interval calls in src/app.ts.
  • When to set: - 24 (default) is the right value for typical interactive REPL and dev work — gives the LLM plenty of time to read_file an earlier turn's spill. - 168 (7 days) is the audit-friendly value for compliance-bound deployments where the spilled tool outputs must survive long enough for a quarterly review or regulator look-back. - 1 is the aggressive-cleanup value for ephemeral CI runs.
  • When unset: 24h.
  • Format: positive integer in [1, 720] (1h to 30 days). Anything malformed / out-of-range silently falls back to 24h with a warn.
  • Setting home: Not a user-facing setting

──────────────────────────────────────────────────────────────────── Personalization rebuild — M3.2 event-driven thresholds ────────────────────────────────────────────────────────────────────

CHAT_TURN_RECORDING

enable per-turn recording of (user_message, final_response, tool_calls) into the chat_turns SQLite table.

  • What it controls: the chatTurnRecorder hook in the agent loop ([src/core/agent-loop.ts] fire-and-forget at turn_complete). The recording is the input to the personalization memory- extraction rebuilder — without it, rebuildMemories has nothing to scan and returns no_turns.
  • Consumed by: src/app.ts isChatTurnRecordingEnabled() — toggles whether the hook is registered on the agent loop.
  • When to set: leave default ON for production. Set to 0 only in privacy-sensitive deployments or when debugging a turn loop in isolation from the personalization layer.
  • When unset: default ON — every turn is persisted.
  • Format: 0 / false / no / off disables. Any other value (including empty or unset) keeps it on.
  • Setting home: Settings → Preferences (schema key)

FIN_PROFILE_TRADING_SUMMARY_MIN_NEW_TRADES

minimum number of new trades required before the trading-summary rebuilder will consider running. One of two gates (the other is the min check interval below); BOTH must be satisfied.

  • What it controls: the tradingSummaryMinNewTrades threshold in rebuildTradingSummary ([src/memory/personalization-rebuilder.ts]). Also informs the scheduler's 60-min safety-net tick — when the tick fires but maxTradeId - last_seen < threshold, the rebuild silently no-ops without hitting the LLM.
  • Consumed by: rebuildTradingSummary in the rebuilder.
  • When to set: raise to 5+ for users who place many small swaps and find the summary churning too often; drop to 1 for low-volume users who want the summary refreshed immediately on any trade.
  • When unset: default 3 — rebuild only after 3 new trades accumulate.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_TRADING_SUMMARY_MIN_INTERVAL_MIN

minimum minutes between two successful trading-summary rebuilds. Paired with the new-trades threshold above.

  • What it controls: tradingSummaryMinCheckIntervalMs. Replaces the legacy tradingSummaryCooldownMs semantics.
  • Consumed by: rebuildTradingSummary gate 1.
  • When to set: lower (10-15) for power-users who want summary to converge quickly; raise (60-120) to reduce LLM cost.
  • When unset: default 30 minutes.
  • Format: positive integer (minutes).
  • Setting home: Not a user-facing setting

FIN_PROFILE_TRADING_SUMMARY_MAX_TRADES

max trades considered per rebuild in FULL-regen mode (cold start or force=true).

  • What it controls: the upper bound on trades passed to the LLM when no prior summary exists. Incremental merges use the separate INCREMENTAL_MAX_TRADES window below.
  • Consumed by: rebuildTradingSummary full-regen path.
  • When unset: default 100.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_TRADING_SUMMARY_INCREMENTAL_MAX_TRADES

max NEW trades fed to the LLM in incremental-merge mode (when a prior summary exists). Kept small so each rebuild is cheap.

  • What it controls: tradingSummaryIncrementalMaxTrades. The LLM sees (existingSummary, newTrades) and merges them.
  • When unset: default 50.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_TRADING_SUMMARY_MAX_INCREMENTAL_RUNS

how many consecutive incremental merges may run before the rebuilder forces a full regen. Incremental mode rewrites its own prior summary from just the new-trade delta, so small errors and phantom "long-term patterns" compound over many merges; a periodic full regen re-reads the real trade history and re-anchors the summary.

  • What it controls: tradingSummaryMaxIncrementalRuns. A per-user counter (financial_profile.trading_summary_incremental_runs) increments on each incremental merge and resets to 0 on a full regen; when it reaches this cap the next rebuild is a full regen.
  • When unset: default 10.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_MEMORIES_MIN_NEW_TURNS

minimum new chat turns required before rebuildMemories will scan. Parallels the trading-summary new-trades threshold.

  • What it controls: memoriesMinNewTurns. The second gate (alongside the min check interval) for the memory-extraction rebuilder.
  • Consumed by: rebuildMemories gate 2.
  • When to set: lower (2-3) for chatty users who generate facts quickly; raise (10+) for slow-paced single-question conversations.
  • When unset: default 5.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_MEMORIES_MIN_INTERVAL_MIN

minimum minutes between two successful memories-extraction rebuilds.

  • What it controls: memoriesMinCheckIntervalMs.
  • When unset: default 10 minutes.
  • Format: positive integer (minutes).
  • Setting home: Not a user-facing setting

FIN_PROFILE_EVENT_DEBOUNCE_SEC

debounce window for event-driven scheduleCheck(dim) calls. Collapses rapid bursts of data-write events (e.g. 5 trades in 500ms) into a single gate-checked rebuild attempt after the window elapses.

  • What it controls: eventDebounceMs. The rebuilder reuses the same setTimeout per dimension; a fresh event within the window resets the timer.
  • Consumed by: PersonalizationRebuilder.scheduleCheck.
  • When to set: raise to 60+ for backtest / CI scenarios where many simulated trades land in quick succession; lower to 5-10 for developers who want near-instant rebuild feedback while debugging.
  • When unset: default 30 seconds.
  • Format: positive integer (seconds).
  • Setting home: Not a user-facing setting

Off-agent history mirror

The personalization rebuilder now consumes three sources: local trade_history (agent in-session), perps_fills (cross-sub mirror of Minara's /v1/perp-wallets/fills), and external_spot_activities (Minara's /v1/tx/cross-chain/activities). The knobs below tune how the mirror keeps itself up to date and how much of it the LLM rebuilder reads.

FIN_PROFILE_HISTORY_SYNC_WINDOW_DAYS

rolling sync window in days.

  • What it controls: cutoff for both perps fills and spot activities. Anything older than now - WINDOW_DAYS is never pulled.
  • Consumed by: MinaraHistorySync and the trading-summary aggregate queries via tradingSummaryAggregateWindowDays.
  • When to set: increase for traders who want longer-term style capture; decrease for high-frequency users whose mirror would otherwise grow into the tens of thousands of rows.
  • When unset: default 90 days.
  • Format: positive integer (days).
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_MIN_INTERVAL_MIN

throttle floor between successive sync triggers. Multiple scheduleSync() calls inside the window collapse to a single eventual run. Protects against turning every trade event into an upstream API hit.

  • Consumed by: MinaraHistorySync.scheduleSync + runIfStale.
  • When unset: default 5 minutes.
  • Format: positive integer (minutes).
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_TIMEOUT_SEC

hard timeout for one syncAll() call. Routed through an AbortController so the underlying HTTP calls are cancelled, not just orphaned.

  • When unset: default 8 seconds.
  • Format: positive integer (seconds).
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_PAGE_HINT

upstream "this page is probably full" heuristic for the perps-fills endpoint, which lacks any pagination parameter. When getPerpSubAccountFills returns >= this many rows we assume there might be more in the same window and slide startTime forward to ask again.

  • When unset: default 500.
  • Format: positive integer (rows).
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_OVERLAP_SEC

overlap (seconds) when sliding startTime forward on a probably-truncated page. Larger overlap = more wasted fetches; smaller = higher risk of skipping a fill at the boundary. fill_uid dedup makes double-counting harmless.

  • When unset: default 60 seconds.
  • Format: positive integer (seconds).
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_MAX_ROUNDS_PER_SUB

hard upper bound on the truncation-rolling loop per sub-account. After this many rounds we bail and let the next sync continue from the watermark.

  • When unset: default 10.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_MAX_FAILURES

per-(source, sub_account_id) consecutive failure threshold. At/above this count, the sync skips that key during normal scheduling; the watermark stays put so the next attempt resumes from the same place. The skip is NOT permanent — see _FAILURE_COOLDOWN_MIN.

  • When unset: default 5.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_FAILURE_COOLDOWN_MIN

after a key hits MAX_FAILURES, the next probe attempt is gated on this cooldown elapsing since last_synced_at. Probes succeed → counter resets to 0; probes fail → counter keeps incrementing. Prevents a transient outage from permanently disabling the mirror.

  • When unset: default 30 minutes.
  • Format: positive integer (minutes).
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_SPOT_MAX_PAGES

hard cap on the spot pagination loop. Stops runaway pagination if the upstream returns a constant page-full response.

  • When unset: default 20.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_HISTORY_SYNC_SPOT_PAGE_SIZE

spot pagination batch size. Forwarded to Minara as limit. Must be supported by the upstream API; 100 is the documented default.

  • When unset: default 100.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_TRADING_SUMMARY_PERPS_RECENT_FILLS

how many of the most recent perps fills are sent to the LLM rebuild. Per-symbol aggregate is always sent in full; this knob just bounds the raw-fill log.

  • When unset: default 30.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_TRADING_SUMMARY_SPOT_RECENT_ACTIVITIES

same for spot.

  • When unset: default 20.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

FIN_PROFILE_TRADING_SUMMARY_AGGREGATE_WINDOW_DAYS

window for the per-symbol / per-pair aggregates fed to the LLM rebuild. Typically equal to HISTORY_SYNC_WINDOW_DAYS; can be smaller to focus on recent behaviour.

  • When unset: default 90.
  • Format: positive integer (days).
  • Setting home: Not a user-facing setting

FIN_PROFILE_MEMORY_SOFT_DELETE_RETENTION_DAYS

how long soft-deleted memories stay recoverable before the 30-min purge cron physically removes them. The web UI's "Undo delete" toast restores within 5 seconds; this knob protects against longer-term accidental deletions by keeping the row around so a CLI-level restore can still bring it back.

  • When unset: default 30 days.
  • Format: positive integer (days).
  • Setting home: Not a user-facing setting

MINARA_HL_DEX_DISCOVERY

opt in to live Hyperliquid perpDexs discovery when syncing perps positions, orders, and history.

  • What it controls: the per-sweep dex fan-out for the perps snapshot and history sync. Default queries only the two dexes the current user cohort is known to hold positions on ("" default + "xyz" stocks/commodities) — 16 HL requests per sweep, well within HL's per-IP rate limit. When this flag is on, the snapshot also calls HL perpDexs (cached for 10 minutes) and fans out across every named dex it returns (xyz, flx, vntl, hyna, km, abcd, cash, para, ...). For the typical 4-sub user that pushes the per-sweep budget to ~72 requests, which reliably 429s the public /info endpoint.
  • When to set: only if you actually hold positions on a named dex outside the "" / "xyz" pair (e.g. flx, vntl). Most users should leave this off.
  • When unset: only the known-good ["", "xyz"] pair is queried and the per-sub clearinghouseState + frontendOpenOrders fan-out stays inside HL's rate-limit budget.
  • Format: 1 / true / yes / on to enable; anything else (or unset) leaves discovery disabled.
  • Setting home: Settings → Preferences (schema key)

WORKFLOW_HEARTBEAT_CHECK_MS

workflow heartbeat monitor sweep cadence. What it controls: HeartbeatMonitor.checkIntervalMs ([src/workflow/heartbeat.ts]). A running instance whose last step updateInstance is older than MAX_SILENCE_MS gets marked status="canceled" cancel_reason="timeout".

  • When to set: shorter in dev/staging (e.g. 5000) to surface hangs fast, longer in production to reduce SQLite scan load.
  • When unset: defaults to 30 000 ms (30 seconds).
  • Format: positive integer (milliseconds). Non-numeric / non-positive falls back to default.
  • Setting home: Not a user-facing setting

WORKFLOW_HEARTBEAT_MAX_SILENCE_MS

silence threshold before a running workflow is considered timed out.

  • What it controls: HeartbeatMonitor.maxSilenceMs. Also bounds the max tolerable single-step duration because last_heartbeat_ms is bumped on every commitStepAdvance.
  • When to set: raise above 300 000 ms default for workflows with long agent_turn steps or external-API fetches.
  • When unset: defaults to 300 000 ms (5 minutes).
  • Format: positive integer (milliseconds).
  • Setting home: Not a user-facing setting

WORKFLOW_MAX_QUEUE_DEPTH

per-definition cron/event fire queue ceiling ([src/workflow/triggers.ts]). Each workflow definition has its own FIFO; full queue drops OLDEST pending fire.

  • When to set: raise for workflows whose trigger can legitimately outrun single-run duration and you care about catch-up; lower to 1 when only the newest signal matters.
  • When unset: defaults to 10.
  • Format: positive integer.
  • Setting home: Not a user-facing setting

MINARA_SS_CODEGEN_MAX_ITER

how many "generate code → ephemeral backtest → refine" iterations the offline strategy-codegen subagent is allowed per benchmark run.

  • What it controls: the step budget of the codegen loop in runStrategyCodeSubagent ([src/core/strategy-code-subagent.ts]). The subagent runs to this many steps. The model decides when to backtest and when to stop within that budget; on return, success is recomputed from the final backtest (status COMPLETED + non-zero trades + drawdown < 0.95).
  • Consumed by: runStrategyCodeSubagent in src/core/strategy-code-subagent.ts, driven by the strategy-rl benchmark runner (offline tuning only; not a chat-facing tool).
  • Default when unset: 3
  • Clamped to: [1, 10]
  • Format: positive integer
  • Operators raise this when their llmClient is fast and cheap (e.g. Haiku) and they want better convergence; lower (1–2) when using a slow / expensive model and willing to accept worse code.
  • Setting home: Not a user-facing setting

MINARA_STRATEGY_SKILL_RL_ENABLED

opt-in pilot for Strategy Studio Skill RL / self-evolving Skill text.

  • What it controls: store-backed Strategy Studio skill versions, per-user strategy preference overlays, rollout tracing, and reward logging for the offline codegen benchmark loop.
  • Default when unset: disabled. When disabled, bootstrap does not create the strategy_skill_* tables and does not alter the production Strategy Studio skill prompt or codegen policy.
  • Accepted truthy values: 1 | true | yes | on
  • Scope: Strategy Studio only in this pilot; this is not a global RL harness for every skill.
  • Setting home: Settings → Preferences (schema key)

MINARA_STRATEGY_CONTEXT_RL_ENABLED

opt-in Strategy Studio Context Policy RL pilot.

  • What it controls: versioned external-context selection policies, policy-aware context collection, three-arm replay benchmarks, and explicit promotion / rollback audit records.
  • Default when unset: disabled. Requires MINARA_STRATEGY_SKILL_RL_ENABLED=1. When disabled, bootstrap does not create strategy_context_policy_* tables and the external-context provider remains unchanged.
  • Accepted truthy values: 1 | true | yes | on
  • Scope: Strategy Studio external-context selection only.
  • Setting home: Settings → Preferences (schema key)

MINARA_SKILL_ROUTER_RL_ENABLED

opt-in general Agent Harness RL pilot for Skill Router policy evolution.

  • What it controls: versioned skill-ranking policies, offline routing benchmark cases, bounded policy exploration, explicit promotion / rollback, and promoted-policy ordering of the per-turn Skill catalog.
  • Default when unset: disabled. Bootstrap does not create skill_router_* tables, the Skill catalog stays in its existing priority order, and the builtin did-you-mean routing behavior is unchanged.
  • Accepted truthy values: 1 | true | yes | on
  • Scope: general Skill discovery/routing only. It never changes Skill text, tool permission tiers, safety gates, or model weights.
  • Setting home: Settings → Preferences (schema key)

───────────────────────────────────────────────────────────────── Institution Mode (multi-agent firm simulation) ───────────────────────────────────────────────────────────────── The minara_institution_analyze tool convenes a 6-phase pipeline (4 analysts in parallel → bull/bear research debate → research manager → trader → 3-way risk debate → portfolio manager) for high-stakes single-asset analysis. Modeled after TradingAgents (https://github.com/TauricResearch/TradingAgents). Default values for round counts are anchored to TradingAgents' default_config.py. Timeouts / token caps follow Minara practice (deep-research stage budgets, agent-loop max_tokens). All vars below are agent-loop infrastructure — no MINARA_ prefix per the project's env-var naming convention.

INSTITUTION_MAX_DEBATE_ROUNDS

bull vs bear alternating rounds.

  • What it controls: number of full rounds in Phase 2 of the institution pipeline. 1 round = 2 turns total (one bull, one bear). Higher → richer debate, more LLM cost.
  • Consumed by: runInstitution in src/tools/institution/orchestrator.ts
  • Surfaced via tool: minara_institution_analyze (max_debate_rounds arg overrides this for one call)
  • Default when unset: 1 (matches TradingAgents max_debate_rounds: 1)
  • Clamped to: [1, 5]
  • Format: positive integer
  • When to set: bump to 2 for higher-conviction trades where you want the bull and bear to engage twice; leave at 1 for routine analyses to keep cost in check.
  • Setting home: Not a user-facing setting

INSTITUTION_MAX_RISK_ROUNDS

aggressive/conservative/neutral rotation rounds.

  • What it controls: number of full rounds in Phase 5. 1 round = 3 turns total (Aggressive → Conservative → Neutral). Higher → more thorough risk scrutiny, more LLM cost.
  • Consumed by: runInstitution in src/tools/institution/orchestrator.ts
  • Surfaced via tool: minara_institution_analyze (max_risk_rounds arg overrides this for one call)
  • Default when unset: 1 (matches TradingAgents max_risk_discuss_rounds: 1)
  • Clamped to: [1, 5]
  • Format: positive integer
  • Setting home: Not a user-facing setting

INSTITUTION_WALL_CLOCK_TIMEOUT_MS

global wall-clock budget for one institution run.

  • What it controls: hard upper bound on the total elapsed time of a single minara_institution_analyze call. When exceeded the orchestrator short-circuits any in-flight phases and returns whatever completed plus meta.truncated: true. Per-call timeout for individual sub-LLM calls is governed by INSTITUTION_PER_CALL_TIMEOUT_MS (see below) and is clamped to never exceed this wall clock.
  • Consumed by: runInstitution in src/tools/institution/orchestrator.ts
  • Default when unset: 1200000 (20 min — gives enough headroom for two sequential 5-minute calls plus the rest of the pipeline)
  • Clamped to: [60000, 1800000]
  • Format: integer milliseconds
  • When to set: lower (e.g. 600000 = 10 min) for cheaper / faster runs that accept partial results; raise (e.g. 1800000 = 30 min) when running large debates with the deep model.
  • Setting home: Not a user-facing setting

INSTITUTION_PER_CALL_TIMEOUT_MS

per-LLM-call timeout inside the institution pipeline.

  • What it controls: per-call abort signal applied to every sub-LLM call (analysts, debaters, managers, structured-output retries). Analysts that need to call 2-3 data tools and write a structured AnalystReport in a single sub-agent loop routinely take longer than a tight cap — the prior 60s default (wallClock/10) aborted them mid-write. The new 300s default leaves enough budget for that work while still guarding against stuck providers.
  • Consumed by: perCallTimeoutMs in src/tools/institution/orchestrator.ts (also threads into runInstitutionSubagent)
  • Default when unset: 300000 (5 min)
  • Clamped to: [5000, INSTITUTION_WALL_CLOCK_TIMEOUT_MS]
  • Format: integer milliseconds
  • When to set: lower (e.g. 60000 = 1 min) when running on a fast quick-model and you want analysts to fail-fast rather than burn wall clock; raise (e.g. 600000) only with a matching wall clock bump if your provider responds slowly.
  • Setting home: Not a user-facing setting

INSTITUTION_MAX_OUTPUT_TOKENS_PER_TURN

per-LLM-call output cap.

  • What it controls: max_tokens ceiling applied uniformly to every LLM call inside the institution pipeline (analysts, debaters, managers, structured-output retries). Acts as a cost ceiling — not a behavioral knob. Each role's prompt asks for concise output anyway; this just guards against runaway generation.
  • Consumed by: runInstitution and runInstitutionSubagent in src/tools/institution/
  • Default when unset: 4096 (matches the agent loop's default max_tokens)
  • Clamped to: [1024, 16384]
  • Format: positive integer
  • When to set: lower for cost control during experimentation; raise when the PM keeps truncating the executive_summary.
  • Setting home: Not a user-facing setting

───────────────────────────────────────────────────────────────── Institution Mode v2 — self-learning (PR 1 + PR 2) ───────────────────────────────────────────────────────────────── Persistence + Phase B reflection ladder + Phase 0 retrospective refresh. The runtime data stays in the same SQLite db as the rest of Minara. Reflection alpha numbers feed PR 4's methodology graduation feedback loop (lands in a follow-up).

INSTITUTION_LEARNING_ENABLED

master switch for the v2 self- learning path (institution_runs / institution_role_outputs / institution_reflections persistence).

  • What it controls: when on, every minara_institution_analyze run fire-and-forget persists its full structured artifacts (PM rating + thesis + per-role outputs + methodology refs). When off, the capture hook no-ops and the institution_* tables stay empty.
  • Consumed by: captureInstitutionRun in src/learning/institution/capture-hook.ts
  • Default when unset: on
  • Format: on | off | 1/true/yes — anything else (including unset) → on
  • When to set: only set to off for ephemeral / CI / dev runs where you don't want any institution row written to disk. Production leaves this on; the persisted rows feed Phase B reflection + methodology graduation downstream.
  • Setting home: Settings → Preferences (schema key)

INSTITUTION_RETROSPECT_ENABLED

Phase 0 lazy refresh master switch.

  • What it controls: when on, every new minara_institution_analyze call walks recent runs on the same (ticker, asset_class) and writes a lazy_refresh reflection on any whose latest reflection is stale. Operators can inspect them via /institution-history.
  • Consumed by: runSingleTicker in src/tools/institution/index.ts (Phase 0 block), runLazyRefresh in src/learning/institution/reflect.ts
  • Default when unset: on
  • Format: on | off
  • When to set: turn off in CI / batch tests where the YahooFinance network call would slow runs without value. The standard cron reflection (if scheduled) keeps running independently.
  • Setting home: Settings → Preferences (schema key)

INSTITUTION_RETROSPECT_LIMIT

Phase 0 history depth.

  • What it controls: maximum number of prior runs Phase 0 will pull per (ticker, asset_class) before deciding which to lazy-refresh.
  • Consumed by: runSingleTicker in src/tools/institution/index.ts
  • Default when unset: 10
  • Clamped to: [1, 50]
  • Format: positive integer
  • When to set: lower (3-5) for chatty multi-ticker users to bound per-call latency; raise (20+) when reflective context is more valuable than freshness on a small ticker set.
  • Setting home: Not a user-facing setting

INSTITUTION_RETROSPECT_TIMEOUT_MS

Phase 0 wall clock.

  • What it controls: hard upper bound on the total time Phase 0 spends on lazy-refresh writes per institution call. Effective timeout is min(this var, INSTITUTION_WALL_CLOCK_TIMEOUT_MS / 2) with a 5s floor — guarantees the orchestrator (Phase 1-6) still has at least 50% of its declared wall-clock budget when Phase 1 starts. Without this cap a 60s tool-call budget could end up taking 180s+ total (codex review round 2).
  • Consumed by: runSingleTicker in src/tools/institution/index.ts
  • Default when unset: 120000 (2 min)
  • Clamped to: [5000, INSTITUTION_WALL_CLOCK_TIMEOUT_MS / 2]
  • Format: positive integer milliseconds
  • Setting home: Not a user-facing setting

INSTITUTION_LAZY_REFRESH_STALE_HOURS

Phase 0 staleness threshold.

  • What it controls: a prior run's most-recent reflection is treated as "stale" (and lazy-refresh-eligible) when its evaluated_at is older than this many hours. Smaller values write more refreshes per call (= higher cost + more current PM context); larger values rely on the daily cron to keep things fresh.
  • Consumed by: shouldWriteLazyRefresh in src/learning/institution/reflect.ts
  • Default when unset: 24
  • Clamped to: [1, 168]
  • Format: positive integer hours
  • Setting home: Not a user-facing setting

INSTITUTION_LAZY_REFRESH_DEDUPE_HOURS

Phase 0 write de-dupe.

  • What it controls: minimum hours between consecutive lazy_refresh writes on the same run. Prevents /institution BTC called three times in 10 minutes from generating three near-identical reflections — the second + third calls reuse the first row.
  • Consumed by: shouldWriteLazyRefresh in src/learning/institution/reflect.ts
  • Default when unset: 6
  • Clamped to: [1, 48]
  • Format: positive integer hours
  • Setting home: Not a user-facing setting

INSTITUTION_AUTO_STALE_DAYS

auto-stale promotion threshold.

  • What it controls: an open institution run is promoted to auto_stale when its age exceeds this many days WITHOUT being finalized. Auto-stale runs continue to receive Phase B reflections but get downweighted in the PM past_context injection (PR 2 downstream wiring).
  • Consumed by: runScheduledReflections in src/learning/institution/reflect.ts
  • Default when unset: 90
  • Clamped to: [30, 365]
  • Format: positive integer days
  • Setting home: Not a user-facing setting

INSTITUTION_BENCHMARK_CRYPTO / INSTITUTION_BENCHMARK_STOCK / INSTITUTION_BENCHMARK_FOREX

alpha benchmarks per asset class. Phase B reflection alpha is computed as raw_return - benchmark_return over the same window. Set to a ticker the configured price source can resolve (Yahoo by default).

  • Defaults when unset: crypto → BTC stock → SPY forex → DXY
  • (commodity / stablecoin / unknown deliberately have no benchmark
  • — alpha is recorded as null for those classes.)
  • Format: ticker string
  • Setting home: Not a user-facing setting

Analyst Recovery

Each Phase-1 analyst slot runs the model in a free tool-calling loop, then runs a single synthesis turn that asks for a structured prose summary in HEADLINE / KEY FINDINGS / CONFIDENCE format. The orchestrator parses the prose directly into an AnalystReport — no forced-submit toolChoice step, no retry harness. When the synthesis is empty or unparseable, the orchestrator falls back to buildSubagentSummaryReport which always emits a usable report (either from raw tool outputs when tools succeeded, or with role-default reasoning when they didn't). Downstream phases always receive a usable summary; the legacy data_gap flag has been retired. No operator-tunable env vars — the contract is "always produce something usable" and there's no budget to tune.

INSTITUTION_FORCE_RESOLVER_PREFLIGHT

always-on canonical preflight.

  • What it controls: by default, the server-side canonical-identity preflight runs only when classifyAsset(ticker) === "unknown". Setting this to true forces preflight for every ticker, which is useful for ops validation (does the resolver behave correctly on well-known tickers too?). Has a small latency cost (~50ms cache hit, ~200-500ms cache miss).
  • Consumed by: orchestrator preflight stage.
  • Default when unset: false
  • Accepted values: true / false / 1 / 0
  • Setting home: Not a user-facing setting

Canonical Asset Cache

The canonical-asset resolver writes through a SQLite cache so the second time the agent sees a ticker it doesn't re-hit CoinGecko / CMC / DexScreener. TTL is per-outcome — a unique resolution can stay cached longer than an ambiguous one, and an empty result expires quickly so we re-try after providers publish more data.

CANONICAL_ASSET_CACHE_TTL_RESOLVED_DAYS

TTL for outcome: "resolved" (single canonical chain+contract or native+chain).

  • Consumed by: src/learning/canonical-asset-cache.ts
  • Default when unset: 30
  • Format: positive integer days
  • Setting home: Not a user-facing setting

CANONICAL_ASSET_CACHE_TTL_MULTI_DAYS

TTL for outcome: "multi" (multi-chain deployment, e.g. USDC on 20 chains). Shorter than resolved because operator/user disambiguation may pin to a specific chain.

  • Consumed by: src/learning/canonical-asset-cache.ts
  • Default when unset: 14
  • Format: positive integer days
  • Setting home: Not a user-facing setting

CANONICAL_ASSET_CACHE_TTL_AMBIGUOUS_DAYS

TTL for outcome: "ambiguous" (provider results disagree, multiple token candidates). Short so we re-resolve after provider data converges or a user override pins.

  • Consumed by: src/learning/canonical-asset-cache.ts
  • Default when unset: 7
  • Format: positive integer days
  • Setting home: Not a user-facing setting

CANONICAL_ASSET_CACHE_TTL_NONE_DAYS

TTL for outcome: "none" (no source returned data). Very short — providers update daily, so we want a fresh attempt soon rather than caching a negative.

  • Consumed by: src/learning/canonical-asset-cache.ts
  • Default when unset: 1
  • Format: positive integer days
  • Setting home: Not a user-facing setting

CANONICAL_ASSET_CACHE_TTL_USER_DAYS

TTL for user-supplied entries (operator override via minara assets pin or banner CTA). Long because the user has explicitly told us what canonical they want; pin entries supersede provider resolution.

  • Consumed by: src/learning/canonical-asset-cache.ts
  • Default when unset: 365
  • Format: positive integer days
  • Setting home: Not a user-facing setting

CANONICAL_ASSET_CACHE_FALLBACK_TO_EXPIRED

stale fallback policy.

  • What it controls: when the live aggregator fails (all providers down or rate-limited), the resolver can either (a) return the expired cache entry tagged from_expired_fallback: true (true, default), or (b) treat the failure as outcome: "none" (false). Stale data is usually better than no data for a few-day-old token resolution; set false if your deployment cannot tolerate any drift.
  • Consumed by: src/learning/canonical-asset-resolver.ts
  • Default when unset: true
  • Accepted values: true / false / 1 / 0
  • Setting home: Not a user-facing setting

Hybrid Memory Retrieval

These four vars enable embedding-augmented memory search. When EMBEDDING_PROVIDER is disabled (the default) the store leaves every row's embedding_state='pending' with a NULL embedding column and searchMemoriesHybrid falls through to the pre-Phase-A BM25 path with byte-identical results. Operators only enable this when they want the semantic-recall lift — at which point every writeMemory / writeRoleMemory schedules an async embedding via queueMicrotask (no write-path latency cost). Failures NEVER throw to the caller: the embedding_state column moves to 'failed' / 'skipped' so minara doctor can detect heaps and minara doctor --fix --apply can backfill them later.

EMBEDDING_PROVIDER

which provider to call.

  • Consumed by: createEmbeddingProviderFromEnv in src/memory/embedding-provider.ts
  • Surfaced to: MemoryStore.searchMemoriesHybrid, MemoryStore.scheduleEmbedding, MemoryStore.backfillEmbeddings
  • Accepted values: disabled | openai | voyage
  • Default when unset: disabled
  • When disabled, the factory returns null — the hybrid code path is
  • completely inert and behaves like pre-Phase-A code. Set to a real
  • provider only when EMBEDDING_API_KEY is also configured; otherwise
  • the factory still returns null (with a logged warn).
  • Setting home: Not a user-facing setting

EMBEDDING_API_KEY

bearer token for the configured provider.

EMBEDDING_MODEL

model identifier.

  • Defaults track each provider's cheapest 1536-dim model so vec0 table
  • sizes stay aligned with EMBEDDING_DIM's default: - openai → text-embedding-3-small - voyage → voyage-3
  • Override only when you've validated dimensions match
  • EMBEDDING_DIM; mismatches are rejected at embed time (logged warn,
  • row stays 'failed').
  • Setting home: Not a user-facing setting

EMBEDDING_DIM

vector dimensionality.

  • Must match the model. Default: 1536. Used to declare the vec0
  • virtual tables (memories_vec, role_memory_vec) at boot —
  • changing this on an existing DB requires manual migration of the
  • vec0 tables (drop + recreate; embeddings are non-authoritative so
  • loss is recoverable via doctor --fix --apply).
  • Format: positive integer.
  • Setting home: Not a user-facing setting

EMBEDDING_BASE_URL

optional override for the provider HTTP endpoint.

SQLITE_VEC_EXTENSION_PATH

explicit path to the sqlite-vec loadable extension binary.

  • Consumed by: MemoryStore.tryEnableVec in src/memory/memory-store.ts
  • Default when unset: the sqlite-vec npm package's bundled platform
  • binary (resolved via getLoadablePath()). Operators only set this
  • when self-managing the binary (e.g. shared system path, a custom
  • build, or a Docker layer that strips node_modules of the sibling
  • sqlite-vec-<platform>-<arch> package).
  • Format: absolute filesystem path to the .so/.dylib/.dll. Failure to
  • load is non-fatal — the hybrid path silently degrades to pure BM25.
  • Setting home: Not a user-facing setting

On this page

BACKTEST_ENABLEDBACKTEST_DRY_RUNBACKTEST_MIN_TRADE_AGE_MSBACKTEST_OUTCOME_HORIZON_HOURSBACKTEST_BATCH_LIMITBACKTEST_CRON_HOURSBACKTEST_PRICE_PROVIDERBACKTEST_MAX_COST_USD_PER_RUNLEARNING_RECORD_USAGELEARNING_TUNING_ENABLEDPhase 1 — Decision Capture (advice BUY/SELL/HOLD)DECISION_CAPTURE_ENABLEDDECISION_SUMMARIZER_MODELDECISION_SUMMARIZER_TIMEOUT_MSDECISION_CAPTURE_SYNC_MODEDECISION_CAPTURE_HEURISTIC_ENABLEDDECISION_CAPTURE_UNIVERSAL_SCANPhase 2 — Multi-Horizon Decision BacktestDECISION_BACKTEST_ENABLEDMETHODOLOGY_LEARNING_CRON_ENABLEDMETHODOLOGY_LEARNING_CRON_INTERVAL_MSMETHODOLOGY_TUNING_ENABLEDDECISION_REPLAY_ENABLEDDECISION_BACKTEST_DRY_RUNDECISION_BACKTEST_HORIZONSDECISION_BACKTEST_CRON_HOURSDECISION_BACKTEST_MAX_AGE_DAYSHALLUCINATION_MAX_PRICE_DELTA_PCTPhase 3 — Reward ComputationDECISION_HORIZON_WEIGHTS_JSONDECISION_HOLD_NEUTRALITY_THRESHOLDPhase 6 — BO Tuning CycleMETHODOLOGY_INSTANCE_TUNING_ENABLEDMETHODOLOGY_TUNING_CRON_DAYSMETHODOLOGY_TUNING_MAX_BUCKETS_PER_CYCLEMETHODOLOGY_TUNING_PROFILES_PATHMETHODOLOGY_TUNING_MIN_DECISIONS_GLOBALMETHODOLOGY_TUNING_MIN_IMPROVEMENT_RELMETHODOLOGY_TUNING_MAX_SENSITIVITY_DROP_10PCTMETHODOLOGY_TUNING_PARAM_BOUND_RELMETHODOLOGY_TUNING_MIN_CAPTURE_CONFIDENCEPreference Evolution (M2: Financial Auto-Memory)PREFERENCE_LEARNINGPREFERENCE_PROPOSER_INTERVALPREFERENCE_WEEKLY_QUOTAPREFERENCE_DEDUP_THRESHOLDPREFERENCE_PROPOSER_BATCH_SIZEPREFERENCE_MIN_CLUSTER_SIZEPREFERENCE_ASK_COOLDOWN_HOURSPREFERENCE_ASK_MIN_GAP_TURNSPREFERENCE_SKIP_IN_CHAT_ASKM3: keyword scanner + tool-level constraint enforcementPREFERENCE_STYLE_MIN_OBSERVATIONSPREFERENCE_HARD_UNDO_WINDOW_HOURSMINARA_SKIP_FUND_CONFIRMWORKBENCH_REMOTE_WEB_PTY_ENABLEDWORKBENCH_REMOTE_SHELL_PROFILE_POLICYMINARA_DANGEROUSLY_SKIP_PERMISSIONSMINARA_AUTO_REVIEWDISABLE_SCRIPT_RISK_GATEDISABLE_OUTPUT_REDACTIONMINARA_TOOL_RESULT_RETAIN_HOURSCHAT_TURN_RECORDINGFIN_PROFILE_TRADING_SUMMARY_MIN_NEW_TRADESFIN_PROFILE_TRADING_SUMMARY_MIN_INTERVAL_MINFIN_PROFILE_TRADING_SUMMARY_MAX_TRADESFIN_PROFILE_TRADING_SUMMARY_INCREMENTAL_MAX_TRADESFIN_PROFILE_TRADING_SUMMARY_MAX_INCREMENTAL_RUNSFIN_PROFILE_MEMORIES_MIN_NEW_TURNSFIN_PROFILE_MEMORIES_MIN_INTERVAL_MINFIN_PROFILE_EVENT_DEBOUNCE_SECOff-agent history mirrorFIN_PROFILE_HISTORY_SYNC_WINDOW_DAYSFIN_PROFILE_HISTORY_SYNC_MIN_INTERVAL_MINFIN_PROFILE_HISTORY_SYNC_TIMEOUT_SECFIN_PROFILE_HISTORY_SYNC_PAGE_HINTFIN_PROFILE_HISTORY_SYNC_OVERLAP_SECFIN_PROFILE_HISTORY_SYNC_MAX_ROUNDS_PER_SUBFIN_PROFILE_HISTORY_SYNC_MAX_FAILURESFIN_PROFILE_HISTORY_SYNC_FAILURE_COOLDOWN_MINFIN_PROFILE_HISTORY_SYNC_SPOT_MAX_PAGESFIN_PROFILE_HISTORY_SYNC_SPOT_PAGE_SIZEFIN_PROFILE_TRADING_SUMMARY_PERPS_RECENT_FILLSFIN_PROFILE_TRADING_SUMMARY_SPOT_RECENT_ACTIVITIESFIN_PROFILE_TRADING_SUMMARY_AGGREGATE_WINDOW_DAYSFIN_PROFILE_MEMORY_SOFT_DELETE_RETENTION_DAYSMINARA_HL_DEX_DISCOVERYWORKFLOW_HEARTBEAT_CHECK_MSWORKFLOW_HEARTBEAT_MAX_SILENCE_MSWORKFLOW_MAX_QUEUE_DEPTHMINARA_SS_CODEGEN_MAX_ITERMINARA_STRATEGY_SKILL_RL_ENABLEDMINARA_STRATEGY_CONTEXT_RL_ENABLEDMINARA_SKILL_ROUTER_RL_ENABLEDINSTITUTION_MAX_DEBATE_ROUNDSINSTITUTION_MAX_RISK_ROUNDSINSTITUTION_WALL_CLOCK_TIMEOUT_MSINSTITUTION_PER_CALL_TIMEOUT_MSINSTITUTION_MAX_OUTPUT_TOKENS_PER_TURNINSTITUTION_LEARNING_ENABLEDINSTITUTION_RETROSPECT_ENABLEDINSTITUTION_RETROSPECT_LIMITINSTITUTION_RETROSPECT_TIMEOUT_MSINSTITUTION_LAZY_REFRESH_STALE_HOURSINSTITUTION_LAZY_REFRESH_DEDUPE_HOURSINSTITUTION_AUTO_STALE_DAYSINSTITUTION_BENCHMARK_CRYPTO / INSTITUTION_BENCHMARK_STOCK / INSTITUTION_BENCHMARK_FOREXAnalyst RecoveryINSTITUTION_FORCE_RESOLVER_PREFLIGHTCanonical Asset CacheCANONICAL_ASSET_CACHE_TTL_RESOLVED_DAYSCANONICAL_ASSET_CACHE_TTL_MULTI_DAYSCANONICAL_ASSET_CACHE_TTL_AMBIGUOUS_DAYSCANONICAL_ASSET_CACHE_TTL_NONE_DAYSCANONICAL_ASSET_CACHE_TTL_USER_DAYSCANONICAL_ASSET_CACHE_FALLBACK_TO_EXPIREDHybrid Memory RetrievalEMBEDDING_PROVIDEREMBEDDING_API_KEYEMBEDDING_MODELEMBEDDING_DIMEMBEDDING_BASE_URLSQLITE_VEC_EXTENSION_PATH