Commit Graph
334 Commits
Author SHA1 Message Date
Haozhe Wu df70288dee fix(cli): derive version from pyproject, drop hardcoded fallback (#156)
The shipped 0.1.8 wheel reported 0.1.7 because a hardcoded constant was
not bumped on release. The metadata path already fixed installed packages;
this removes the remaining hardcoded fallback so an un-installed checkout
reads the version straight from pyproject.toml — making pyproject the one
and only place the version is written, with no constant left to drift.

- _version: tomllib-based pyproject fallback (>=3.11 guaranteed), "unknown"
  sentinel only if the tree is moved away from its pyproject.toml
- tests: pyproject fallback matches declared version, resolved-not-unknown,
  and a guard against reintroducing any hardcoded version literal
2026-06-01 22:39:57 +08:00
Haozhe Wuandai7eam-dev d2f853233f feat(swarm): retry failed/stale/cancelled runs (HTTP + MCP)
Completes the swarm-retry capability extracted from #159 and makes it
usable end-to-end instead of dead API surface.

- api_server: POST /swarm/runs/{id}/retry — reconciles a stale "running"
  run first, refuses a genuinely active run (409), else re-launches a new
  run with the original preset + user_vars
- mcp_server: retry_run(run_id) tool so the list_runs -> spot-failure ->
  retry loop works from OpenClaw/Claude Desktop (the frontend has no swarm
  dashboard; MCP + HTTP are the real consumers)
- api.ts: retrySwarmRun client method (parity with cancelSwarmRun)
- tests: missing-run / running-refused / happy-path relaunch, MCP well-known
  tool name, and HTTP traversal rejection for the new endpoint
- docs: bump current MCP tool count 35 -> 36 across the 5 READMEs (ar/ko
  prose still carries a stale 22-tool list — tracked separately)

Co-authored-by: ai7eam-dev <ai.7eam@gmail.com>
2026-06-01 22:39:57 +08:00
Haozhe Wuandai7eam-dev a0da6c7727 fix(ui): reliable session running-status indicator
Scoped extraction of the UI running-status indicator from #159 (the
remaining commits there overlapped with #157/#158 or touched the
protected providers/ module).

- useSSE: add attempt.created/started, message.received, session.created,
  tool_heartbeat, llm_usage to knownTypes so a mid-stream reconnect/replay
  still surfaces the working state
- agent store: track streamingSessionId, preserved across switchSession so
  the sidebar spinner persists when navigating away from a running session
- Agent.tsx: handle attempt.created/started (re-affirm streaming on reconnect
  and page reload), keep streaming alive on tool_heartbeat, add a persistent
  pulse bar, reword "Thinking…" to "Agent is working…"
- Layout: replace the dead-code (status === "failed") dot with a spinning
  Loader2 for the actively streaming session
- index.css: pulse-slide keyframe for the running bar

Co-authored-by: ai7eam-dev <ai.7eam@gmail.com>
2026-06-01 22:39:57 +08:00
a8144e9acf feat(frontend): make SSE idle timeout configurable via VIBE_TRADING_SSE_TIMEOUT (#157)
CI / test (push) Waiting to run
Wiki / validate (push) Waiting to run
Deploy Wiki / Deploy to Cloudflare Pages (push) Canceled after 0s
The frontend safety timeout was hardcoded at 90 s, causing false
"Execution timed out" toasts for users running slow local models
(e.g. Ollama on limited VRAM or CPU).

- Backend: expose `sse_timeout_seconds` in `LLMSettingsResponse`,
  reading from `VIBE_TRADING_SSE_TIMEOUT` env var (default 90 s)
- Frontend: fetch the value from `/settings/llm` on mount and use it
  instead of the hardcoded 90 000 ms constant
- `.env.example`: document the new variable in the Agent Tuning section

Co-authored-by: Sky <985369746@qq.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 22:13:15 +08:00
ai7eam-dev f9bea768f4 fix(correlation): normalize timestamps for cross-market asset alignment (#158)
Cross-market correlation (e.g. BTC-USDT vs MSTR) failed with
'No overlapping return data between assets' because different loaders
produce DatetimeIndex values at different UTC offsets:

- OKX/CCXT (crypto): timestamps at UTC midnight (00:00:00)
- yfinance (US equity): timestamps at EDT midnight (04:00:00 UTC)
  After tz_localize(None) the time component is preserved,
  so 04:00 != 00:00 and the inner join produces zero rows.

Fix: call .index.normalize() on each close series before computing
returns, collapsing all timestamps to midnight regardless of source.

Also improve the error message to show actual date ranges per asset
when no overlap is found, making future debugging easier.
2026-06-01 22:13:09 +08:00
Thiên Toán 2a893e1a26 fix(ui): always show Full Report link when runId exists in session history (#150)
* fix(ui): always show Full Report link when runId exists in session history

Previously, when loading a session from history (e.g. in a new browser),
the run_complete card was silently dropped if api.getRun() failed or
returned non-report-worthy data. This caused the "Full Report" link to
disappear for remote/cross-browser users while remaining visible in the
original browser's in-memory cache.

Now the card always renders when runId is present in message metadata,
with metrics and equity curve as optional enrichments. Also improves the
RunDetail error page with diagnostic hints and a back button.

* fix(ui): cast equity to number to satisfy TypeScript strict typing

* fix(ui): distinguish non-report runs from fetch failures in run card logic

Address PR review: not every run_id is a backtest — plain chat turns also
get a run_id. Now only show the Full Report card when getRun() confirms
report-worthiness OR when the fetch fails (auth/network). Plain chat turns
where getRun() succeeds but isReportWorthyRun is false correctly skip the
card, avoiding spurious links.
2026-06-01 22:12:50 +08:00
Haozhe Wu 1e3bd7a8c0 chore(trading): clean connector docs and on-ramp mapping
Refresh multilingual MCP tool counts and centralize broker-to-connector on-ramp profile resolution so the frontend no longer duplicates broker profile fallbacks.
2026-06-01 00:27:38 +08:00
Haozhe Wu 52f5059159 feat(trading): add connector-first broker profiles
Add unified trading connector profiles and tools for IBKR and Robinhood. Gate live runner behavior by connector capability, preserve IBKR local read-only config, and update CLI, MCP, web UI, tests, skill docs, and multilingual README news.
2026-06-01 00:17:28 +08:00
Haozhe Wu 4e009c7769 docs: news 2026-05-30 — backtest/agent-loop/session robustness pass 2026-05-30 21:45:17 +08:00
Haozhe Wu 168122311a fix(backtest): emit clean JSON for signal-engine source errors
Follow-up to #149. Source-level AST validation (circular self-import,
unsafe imports, decorators, top-level statements) raises ValueError from
_load_module_from_file, but the call site was unguarded so it surfaced
as a raw Python traceback -- contradicting #149's stated goal of
actionable JSON errors for the circular-import case. Wrap the call and
emit a clean JSON envelope, unifying all source-validation errors with
the runner's JSON error contract.
2026-05-30 11:46:07 +08:00
Haozhe Wu 7c242b934c fix(loop): guard wrap-up nudge to mid-iterations only
Follow-up to #148. The wrap-up nudge fired at iteration == wrap_up_at
unconditionally, which on tiny budgets (max_iter<=2 -> wrap_up_at==1)
injected a '[SYSTEM] wrap up' user message on the very first iteration,
displacing the active research-goal context as the most recent user
message and breaking test_agent_goal_context. It also conflicted with
goal-continuation by telling the agent to stop early.

Now the nudge only fires for 1 < iteration < max_iterations: never on
the first iteration, and never on the last (the forced text-only path
already guarantees a final answer there).
2026-05-30 11:46:07 +08:00
Thiên Toán 002e042d44 fix(loop): graceful exit on max iterations with wrap-up nudge + forced text-only (#148)
AgentLoop could exhaust all 50 iterations when the LLM kept calling tools,
resulting in status="failed" with no useful output. Mirror the swarm worker's
proven approach: inject a wrap-up nudge at 80% of iteration budget and drop
tool definitions on the last iteration to force a text response.
2026-05-30 11:43:37 +08:00
Thiên Toán a7522d29bb fix(session): handle corrupted JSONL lines and harden message writes (#147)
Truncated writes (from process crash mid-append) produce invalid JSON
lines that crash the GET /sessions/{id}/messages endpoint with a 500.

Write-side: flush + fsync after each append so the full line is
durable before the request continues — prevents data loss for
expensive AI API responses.

Read-side: catch JSONDecodeError per line, log the first 200 chars
for manual recovery, and continue loading the remaining messages
instead of failing the entire endpoint.
2026-05-30 11:43:33 +08:00
ArthurXiandwuyi 86bb254334 Fix IME Enter submission handling (#146)
Co-authored-by: wuyi <wuyi@Wu-Yi-MacBook-Air.local>
2026-05-30 11:18:55 +08:00
Thiên Toán 7dc888eeae fix(backtest): pre-flight validation for LLM-generated signal engines (#149)
Add interface checks before instantiation to catch common LLM mistakes
early with actionable JSON errors instead of raw tracebacks:
- Circular self-import detection (from signal_engine import SignalEngine)
- Required __init__ args check (all params must have defaults)
- Missing generate() method check
- Return-type validation (must be Dict[str, pd.Series], not list)
2026-05-30 11:18:52 +08:00
Haozhe Wu 108bfae960 feat(live): bounded-autonomy live trading channel (Robinhood Agentic Trading)
Add an opt-in, read-only-by-default live execution channel via Robinhood
Agentic Trading (remote MCP, OAuth). The agent trades autonomously only inside
a user-committed mandate (symbol allowlist, order size, exposure, leverage,
instrument, daily cap):

- Mandate model + read-only protected store; commit is the ONLY writer
  (no set_mandate tool reachable from the agent loop)
- Pre-trade enforcement gate (fail-closed) + order-intent extractor + quote pricing
- 3-tier read/write tool classification (default-deny) + OAuth token cache
- Filesystem kill switch (LLM-independent) + preemptive flatten on trip
- Mandate auto-expiry, checked proactively every runtime tick
- Persistent autonomous runtime: scheduler, durable job store, liveness,
  market/interval triggers, crash-recovery reconciliation
- Consent UX (propose/commit/adjust) across CLI and Web UI
- Compliance-grade audit ledger with credential/PII redaction

Read-only/fail-closed by default; protected agent core (src/agent, src/session)
untouched. Paper/mock-tested only; live verification is the community's
(experimental, use at your own risk). Updates all 5 README languages with the
bounded-autonomy disclaimer.
2026-05-29 18:00:08 +08:00
Haozhe Wu f0c799d5b4 docs: news 2026-05-28 — swarm DAG gating + strict alpha bench + worker MCP 2026-05-28 16:40:05 +08:00
Soli22de ec5d8b18d8 feat(factors): strict_bench mode with mandatory random control (#143)
bench_runner.run_bench() labels an alpha "alive" when its raw IC mean
beats 0.02, IC-positive ratio beats 0.55, and the IC self-t-stat clears
2. That gate accepts factors whose IC is driven by shared cross-sectional
beta (market/size/sector leakage) — the test is benchmarked against zero,
not against a same-universe random control, so a row-shuffled version of
the same factor typically clears the gate too.

Adds an opt-in `run_bench_strict()` companion (existing `run_bench()`
untouched and back-compat) that:

1. Builds N same-universe random controls per alpha by row-shuffling the
   factor values within each row — preserves the per-date cross-sectional
   distribution (mean, std, fat tails) while destroying the
   signal-to-instrument mapping. Fairer null than Gaussian noise for IC.
2. Computes a per-date paired alpha series = signal_IC - random_IC and
   evaluates its t-stat instead of the IC self-t-stat.
3. Optionally splits train/test on a user-supplied date and requires the
   test-period alpha_t to also clear the threshold before labelling an
   alpha `confirmed_alive` (otherwise `train_only` for benign decay,
   `reversed_strict` for OOS sign-flip).
4. Makes `random_control: bool` keyword-only with no default — passing
   nothing raises TypeError. Mirrors the rail from Soli22de/Bili_Stock's
   foundation Backtest constructor, which was hardened after a 9-month
   audit found every accidental no-random-control run inflated alpha by
   3-8 pp.

Categories:
  confirmed_alive  alpha_t_full >= thr AND (no OOS OR alpha_t_test >= thr)
  train_only       alpha_t_full >= thr AND alpha_t_test in noise band
  reversed_strict  alpha_t_full <= -thr  OR  OOS sign-flip
  noise            alpha_t_full in (-thr, thr)  OR  ic_count < min_ic_count

Default threshold 2.0 stays numerically comparable with the existing
`categorise()` gate. Pass `StrictThresholds(alpha_t_threshold=3.5)` to
apply the Harvey-Liu-Zhu (2016) multiple-testing correction when running
the full 455-alpha zoo (the Vibe-Trading default zoo size).

Legacy bucket aliases (`alive` / `reversed` / `dead` / `by_theme`) are
preserved on the wire payload so existing dashboards keep rendering
without code changes:
  alive    = confirmed_alive
  reversed = reversed_strict
  dead     = noise + train_only

Tests: 33 in test_bench_strict.py (helpers, categorisation rules,
keyword-only rail, OOS boundary semantics, inf-as-NaN, legacy aliases,
schema-complete error envelope, exception-safe on_progress, planted-
signal integration for both confirmed_alive and reversed_strict).
Full agent/tests/factors/ suite: 1002 passed, 1 skipped — zero
regression in the existing factor tests.
2026-05-28 16:28:57 +08:00
shadowinlife 7acaf74e10 feat(swarm): operator-configured external MCP tools in workers (#142)
Lets swarm workers call tools from operator-approved external MCP
servers in addition to the 31 local Vibe-Trading tools. Useful for
internal knowledge bases, compliance services, or any extra MCP server
the operator wants to attach.

Trust model is preserved at the schema level: MCP server URLs, commands,
env, and allowlists are resolved from the operator's config file
(VIBE_TRADING_SWARM_AGENT_CONFIG / ~/.vibe-trading/swarm-agent.json /
~/.vibe-trading/agent.json), never from `variables` or any other
caller-supplied input. The `run_swarm` MCP entry point cannot inject
server config.

Per-worker registries are built by intersecting each agent's `tools:`
whitelist with the resolved local + remote tool pool. Whitelist entries
the operator hasn't surfaced are dropped with an operator-facing
warning rather than failing the worker. Discovery is narrowed to the
servers actually referenced by the whitelist, so local-only workers
incur no MCP spawn cost.

MCP server-name collisions (e.g. `foo-bar` / `foo_bar`) keep their hash
suffixes after pruning — `_prune_agent_config_for_swarm_tools` resolves
segments against the full config first, then passes the preserved
mapping into `build_registry` so the worker whitelist still matches.

Adds `server` + `remote_tool` metadata to `tool_call` / `tool_result`
events for audit clarity, with nested redaction of sensitive argument
and result values.

Wired into MCP server, API server, CLI live runner, and in-process
SwarmTool. 27 new tests across M1-M5 (config plumbing, registry
assembly, boot wiring, e2e, trust model); 288 broader swarm/mcp/tool
tests pass.
2026-05-28 16:21:32 +08:00
Haozhe Wu 46e49626b9 fix(swarm): wire task_blocked into CLI live panel + carry agent_id
Follow-up to #145. The new task_blocked event was emitted without
agent_id, so the CLI live panel (cli/_legacy.py) could not attach it
to the per-agent row — blocked agents stayed visually "pending" until
the run finalized. Three small fixes:

- runtime.py: pass agent_id=task.agent_id on the task_blocked emit
- cli/_legacy.py: handle task_blocked in the live event loop
- models.py: extend SwarmEvent docstring to mention task_blocked
- test_swarm_dag_gating.py: assert agent_id is present on the event
2026-05-28 15:32:04 +08:00
omcdecor-cyber 5a6780be2b fix(swarm): block downstream tasks when upstream fails (#145)
Bug
---
A failed task in layer N did NOT prevent layer N+1 tasks that declare it
as a dependency from being dispatched. The orchestration loop in
_execute_run set all_succeeded=False on failure but never gated the next
layer. The downstream worker's upstream-summary loop in _execute_layer
silently skipped the missing key (the `if source_task_id in task_summaries`
check), so the worker ran with no upstream context.

This is most visible in the investment_committee preset, where
portfolio_manager depends_on=["task-risk"]: a failed risk_officer
let PM produce a "decision" with no risk input. Reproducer attached as
tests/test_swarm_dag_gating.py shows the same pattern in 3 tasks.

The layer-boundary _sync_run_tasks_snapshot added in #132 surfaces the
failure to polling clients sooner, but it does not gate dispatch — PM
still runs the moment the layer with the failed risk task finishes.

Fix
---
_execute_layer: before dispatch, walk task.depends_on and load each
  upstream from TaskStore. If any upstream has status != completed
  (failed / blocked / cancelled / missing), mark this task
  TaskStatus.blocked, record blocked_by + reason, emit task_blocked,
  and skip executor.submit. Same-layer peers with no shared upstream
  are unaffected (the loop continues).

_execute_run: tasks blocked in _execute_layer are absent from
  layer_results. After the result loop, any layer_task_id missing from
  layer_results was blocked -> set all_succeeded = False so the run is
  marked RunStatus.failed at finalization.

Tests
-----
3 new regression tests in tests/test_swarm_dag_gating.py (TDD red-green
verified before-and-after on this PR's diff):
  - test_failed_upstream_blocks_downstream
  - test_blocked_downstream_emits_task_blocked_event
  - test_run_marked_failed_when_downstream_blocked
Full swarm test suite: 115/115 pass (112 existing + 3 new).
2026-05-28 15:30:52 +08:00
Haozhe Wu bc63784752 docs: bump skill count 75→77 and recommend mootdx as no-token A-share fallback
- 5 READMEs had "75 skills" / "75 个 skills" / similar wording stale
  in 7+ places each; actual `agent/src/skills/` count is 77 after
  today's mootdx skill. Bumped every mention to 77.
- The "no-API-key" tip recommended AKShare as the free A-share
  fallback, but mootdx is now the more reliable option (TCP-direct,
  no IP throttle). Reworded the tip in all 5 languages: mootdx
  preferred, AKShare as broader-coverage backup.
2026-05-27 20:06:41 +08:00
Haozhe Wu 6b1b180f66 docs: news 2026-05-27 — mootdx loader + CCXT proxy + output polish
Add the 2026-05-27 entry to all 5 READMEs and rotate the
2026-05-24 Research Goal runtime entry into Earlier news so the
top-3 stays at 5-27 / 5-26 / 5-25 per the news convention.
2026-05-27 20:00:01 +08:00
Haozhe Wu d542038d36 fix(render): drop markdown HR separators and bump valid_sources
User report (#139): example output shows ugly full-width horizontal
lines instead of clean tables. Root cause is the LLM emitting `---`
between sections, which Rich (CLI) and react-markdown (web) both
render as a full-width separator. Fixed at three layers:

1. System prompt (context.py): explicit rule to use markdown pipe
   tables for multi-row data and `##` headings instead of `---`
   horizontal rules. Also corrected stale "5 data sources" -> "7"
   to reflect mootdx + futu.
2. CLI render_answer (transcript.py): strip standalone `---` /
   `***` / `___` lines as defense-in-depth so historical answers
   and the occasional rule-breaking output render cleanly.
3. Frontend MessageBubble.tsx: add `prose-hr:hidden` so any `<hr>`
   that slips through doesn't draw a line.

Also fixed backtest_tool valid_sources whitelist: `source="mootdx"`
and `source="futu"` were rejected by the validator even though both
loaders were registered. Added both to the allowed set.

Tests: +1 transcript regression covering the HR strip; existing
2385 tests stay green.
2026-05-27 19:53:46 +08:00
Haozhe Wu e4e2a9d9c2 feat(loaders): add mootdx A-share OHLCV loader
Mootdx talks the native 通达信 binary protocol over TCP, bypassing
akshare's East Money HTTP scrape that periodically throttles. No API
token, no per-IP rate limit.

Scope: A-share OHLCV.
- Daily 1D/1W/1M via get_k_data (native date range).
- Intraday 1m/5m/15m/30m/1H via bars() with 25-page walk-back
  pagination (~10y daily, ~5y 1H, ~3mo 1m of lookback).
- SH / SZ supported. 北交所 (BJ) returns empty upstream, so the
  loader detects BJ symbols and skips with a clear warning rather
  than swallowing the failure.
- Mootdx extended market (futures/options) is upstream-broken in
  v0.11.7 — falls through to tushare/akshare for those markets.

Fallback chain becomes a_share: [tushare, mootdx, akshare] so
mootdx serves as the no-token, no-scrape middle tier between
tushare (auth-gated) and akshare (HTTP scrape).

Optional dependency: pip install mootdx && pip install 'httpx>=0.28.1'.
Mootdx pins httpx<0.26 in setup.py but only uses forward-compatible
httpx APIs; the second install restores the modern httpx that the
rest of the project (MCP server, fastmcp) needs.

Closes #107.
2026-05-27 19:43:24 +08:00
ruok808 f607f7a930 feat(loaders): pass proxy env to CCXT (#126)
Read HTTP_PROXY/HTTPS_PROXY/ALL_PROXY (and lowercase variants) and pass them to the CCXT exchange constructor so Binance/OKX public data works from restricted networks. Additive only; no behavior change when no proxy env is set.
2026-05-27 18:42:53 +08:00
Haozhe Wu 3bd356c1f6 fix(deps): bump langgraph for CVE-2026-28277 2026-05-26 00:55:58 +08:00
Haozhe Wu 83801bad46 docs: refresh multilingual news 2026-05-26 00:49:02 +08:00
Haozhe Wu acab70c556 fix(goal): close research goal lifecycle 2026-05-26 00:43:28 +08:00
Haozhe Wu 6b8241a356 fix(wiki): route docs rewrites to docs app 2026-05-25 22:43:33 +08:00
噎 cceb4def1e Fix agent session history loading 2026-05-25 00:53:59 +08:00
liucaiwen 62869fc26a fix(api): accept short universe names in alpha list endpoint
Normalize csi300/sp500/btc-usdt to canonical names before validation
in GET /alpha/list, matching the behavior of the bench form endpoint.

Signed-off-by: liucaiwen <liucaiwenseven@163.com>
2026-05-25 00:53:55 +08:00
Haozhe Wu 8b2273c6de fix(ui): streamline composer goal workflow
Move Research Goal entry into the chat composer: the + menu now switches the
normal input into goal mode, sends create the session goal directly, and the
active goal stays above the input as an inline chip with expandable criteria
and evidence details.

Stabilize the local dev launcher used for browser smoke tests by wiring custom
backend ports into VITE_API_URL, launching long-lived process groups, cleaning
up child processes reliably, and reporting actual frontend/backend status.
2026-05-25 00:41:31 +08:00
Haozhe Wu 2a18d33c23 feat(goal): add research goal runtime
Add the session-scoped Research Goal layer across backend, CLI, API/MCP,
SSE, and Web UI. Goals now persist claims, acceptance criteria, evidence,
budgets, and completion policy; agent tools and /goal can create goals and
attach evidence; REST/MCP expose snapshots and evidence writes; SSE keeps
chat clients fresh.

Also carry the 2026-05-24 transplant cleanup: replay-all/session-id/upload
wiring, report-worthy Full Report gating, English-only Web UI copy, and audit
fixes for verified evidence, live-trading risk tiers, CLI session linkage,
goal ledger cleanup, replay-all hookup, and frontend snapshot races.
2026-05-24 23:55:00 +08:00
Haozhe Wuandykykj 74614f0e7c feat(cli): refresh interactive terminal UI
Refresh the interactive terminal transcript, activity rail, and prompt visuals. Document the 2026-05-23 CLI update in README and keep generated CLI screenshots out of git.

Co-authored-by: ykykj <140159248+ykykj@users.noreply.github.com>
2026-05-24 00:17:45 +08:00
Haozhe Wu 1404eda72b docs(readme): add 2026-05-22 swarm news 2026-05-22 23:45:14 +08:00
Haozhe Wu cac3f0b5ed fix(swarm): unified reconcile + MCP keepalive + heartbeat coverage (#132)
Symptoms reported in #132 (WorkBuddy + DeepSeek MCP):
- MCP run_swarm blocked 30 min then errored, losing run_id
- get_swarm_status / get_run_result showed stale snapshot while tasks/*.json
  had real progress
- Host crash left runs "running" forever with no recovery path
- list_swarm_runs / SSE rendered the same stale snapshot

Architecture:
- store.reconcile_run(write=True): single read-side entry point. Hydrates
  live task files, recovers terminal-but-not-finalized state (host crashed
  between last layer sync and finalize), and reaps stale running runs whose
  events.jsonl has gone silent past compute_stale_threshold.
- Threshold is per-run: max(60, min(heartbeat_interval*10, retry_ceiling))
  — fast detection when heartbeats work, retry-budget-clamped when not.
- HeartbeatTimer wraps registry.execute, llm.stream_chat, AND grounding
  fetch so no normal execution phase can produce >heartbeat_interval of
  silence. Slow first-token / reasoning-mode / pure tool-call responses
  no longer trip the reaper.
- run_swarm emits ctx.report_progress every poll with elapsed seconds, so
  MCP transport stays alive via progress notifications; first frame is
  fixed-format ``swarm_started run_id=<id>`` so transport-drop clients can
  still recover the handle.

Endpoints updated to use reconcile_run:
- MCP: get_swarm_status, get_run_result, list_runs, run_swarm polling
- API: /swarm/runs, /swarm/runs/{id}, /swarm/runs/{id}/events SSE
- SwarmTool: polling + budget-out path (no longer cancels on timeout)

Other fixes:
- SWARM_HEARTBEAT_INTERVAL_S parses with ValueError fallback in worker
  (was crashing import on bad config)
- reaper derives run terminal status from task statuses (no more
  hardcoded "failed" on a run whose tasks all completed)
- serialize_task surfaces started_at / completed_at / depends_on / blocked_by
- reap_stale_runs() MCP tool for explicit user-triggered cleanup

Tests: 22 new regressions in tests/test_swarm_status_hydration.py covering
hydrate, reconcile, terminal recovery, stale reap, MCP keepalive cadence,
auto-recovery on read, threshold math, env var robustness, heartbeat
wiring around LLM + tool execution.

Verification:
- targeted suite: 22 passed
- full swarm/mcp suite: 169 passed, 4 skipped, 0 failed
- compileall + import smoke clean
2026-05-22 11:56:29 +08:00
Haozhe Wu e9c0000f5f chore(providers): refresh default model ids 2026-05-22 10:04:26 +08:00
Haozhe Wu 1edf27e040 ci: switch syntax-check from cli.py to cli/ package
The CLI was split into a package in b5a44a1; the workflow still
ran ``python -m py_compile cli.py`` against the now-renamed file
and failed with "No such file or directory: 'cli.py'".

Use ``python -m compileall -q cli`` so every module under the
package (``main.py``, ``_legacy.py``, ``commands/``, ``components/``,
``utils/``, ``_version.py``, ``__main__.py``) is checked together.
2026-05-22 00:04:00 +08:00
Haozhe Wu b5a44a1e56 refactor(cli): split agent/cli.py into agent/cli/ package
Restructures the 3216-line agent/cli.py into a focused package
while preserving every existing surface verbatim.

Legacy compatibility:
- _legacy.py shim keeps every subcommand (serve, run, hypothesis,
  alpha, memory, provider, swarm, sessions, ...) reachable through
  the same CLI flags and console-script entry.
- Public symbols (cmd_*, _INIT_ENV_PATH, Confirm, AGENT_DIR, ...)
  re-exported through cli.<name>; `from cli import *` exposes 76
  symbols (was 1).
- Package-level cli.RUNS_DIR / cli.SESSIONS_DIR monkeypatches now
  propagate into _legacy before any cmd_* runs.

New scaffolding:
- agent/cli/main.py interactive REPL front door
- agent/cli/commands/ slash router + handlers (15 commands)
- agent/cli/components/ reusable Rich UI building blocks
- agent/cli/{theme,intro,onboard,input,completer,stream}.py
- agent/cli/__main__.py restores `python -m cli`
- agent/cli/_version.py reads importlib.metadata.version() so
  --version output and the banner stay aligned with pyproject.toml

Production wiring:
- New FastAPI middleware in api_server.py serves
  frontend/dist/index.html on browser navigation to /runs/{id}
  and /correlation, regex-narrowed so /runs/{id}/code and
  /runs/{id}/pine API routes stay reachable.
- frontend/vite.config.ts dev proxy mirrors the same narrowing.
- scripts/dev backend starts via package-safe
  `python -c 'import cli; cli.main(...)'`.

Chat-gate narrowed: chat --help / chat -h / chat --max-iter <bad> /
chat extra correctly reach legacy argparse instead of being
swallowed by the new REPL.

Tests: 2271 passed, 43 skipped (focused CLI/SPA test files:
test_cli_interactive.py 67 + test_spa_deep_link.py 13 + existing
CLI memory/init/hypothesis 48 = 128).
2026-05-22 00:00:47 +08:00
Haozhe Wu 34a30fb58d docs(readme): News — Hypothesis Registry CLI (2026-05-20)
Adds a 2026-05-20 entry to all 5 READMEs (EN/ZH/JA/KO/AR) covering the
`vibe-trading hypothesis {list,show,invalidate}` subcommands shipped in
dda06ae. Pushes the 2026-05-17 Alpha Zoo v1 entry into the collapsed
"Earlier news" block to preserve the 3-latest-visible convention.
2026-05-20 22:32:14 +08:00
Haozhe Wu dda06ae351 feat(cli): hypothesis registry — list / show / invalidate
Adds `vibe-trading hypothesis {list,show,invalidate}` subcommands that wrap
the existing HypothesisRegistry storage. List prints a Rich table (or JSON
with --json), supports --status filter and --limit. Show prints a Rich
detail panel including linked run cards. Invalidate flips status to
rejected and optionally records an invalidation_notes message; existing
notes are preserved when --note is omitted.

Closes the roadmap P2 backlog item "Add CLI workflows for listing,
editing, and reviewing hypotheses" on the CLI side; Web workflow waits
on the v0.2.0 IA landing.

22 new tests cover wiring, list/show/invalidate paths, JSON output,
status filter, limit, missing-id errors, and note persistence.
2026-05-20 21:36:22 +08:00
Haozhe Wu 008c0839cd docs(readme): add AI-Trader to ecosystem 2026-05-20 20:40:33 +08:00
Haozhe Wu a1e83aded7 feat: live tool feedback + graceful CLI cancel
Tool calls now emit a 3s heartbeat plus structured per-stage progress
via emit_progress(). run_backtest, read_document, read_url opted in;
all other tools get heartbeat-only feedback automatically.

CLI Rich Live dashboard: Unicode spinner, ASCII progress bar, ETA,
per-tool rows (up to 3 parallel) keyed by tool name. Color discipline
restored (yellow reserved for cancelled/warning; running stays cyan).
0.25s throttle on tool_progress; no-rich path coalesces dots and
progress lines with per-tool gating.

Frontend: new ToolProgressIndicator with rAF coalescing, ARIA
role=status + hidden <progress>, ProgressRing SVG when determinate,
parallel-tool stacking with overflow + truncation. Extracted reusable
primitives ProgressBar.tsx and lib/tools.ts (shared tool-name i18n).

CLI Ctrl+C: first press calls agent.cancel() for graceful exit
(current step finishes, trace closes cleanly); second within 2s
force-quits. Backend POST /sessions/{id}/cancel was already wired.

Also fixes 2 pre-existing baseline test failures:
test_fundamental_filter_example (path resolved via __file__) and
test_openai_codex (pytest.importorskip for optional dep).

Tests: 2174 -> 2176 pass.
2026-05-19 16:19:42 +08:00
Haozhe Wu 0c39711432 docs(readme): add one-line star CTA below star history (5 locales) 2026-05-19 13:35:45 +08:00
Faijan KhanandCursor c1f3898056 fix(providers): normalize remote Ollama base URLs without /v1 (#129)
Append /v1 automatically when LANGCHAIN_PROVIDER=ollama so remote hosts
like http://host:11434 work with ChatOpenAI without manual path suffixes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 12:55:59 +08:00
Haozhe Wu 8a7a658260 fix(cli): vibe-trading init writes to ~/.vibe-trading/.env
Help text and docstring promised ~/.vibe-trading/.env but the
implementation wrote to <AGENT_DIR>/.env, which lives inside
site-packages for pip-installed users and gets wiped on upgrade.

Align the implementation with the documented behavior, mkdir -p
the parent, and chmod 600 the file since it holds API keys.
2026-05-18 16:10:36 +08:00
Haozhe Wu 37fb6e2f4e fix(frontend): show Thinking… placeholder between Send and first SSE event (#128)
Closes #127

Before this change, the live streaming area in Agent.tsx only rendered
when streamingText was non-empty or toolCalls had at least one entry.
The gap between handleSubmit setting status="streaming" and the first
SSE event arriving left the chat area visually blank, making the UI
look unresponsive on slow networks or long agent "thinking" time.

Add a Thinking… placeholder that renders exactly during that gap
(status === "streaming" && !streamingText && toolCalls.length === 0)
and is implicitly hidden once the first SSE event populates either
streaming state.
2026-05-18 15:29:49 +08:00
Haozhe Wu 8a6c578a76 chore: cleanup pass — 3 latent bug fixes + 913 LOC net delete
Bug fixes
- CompositeEngine no longer misroutes bare Chinese-futures codes like
  RB2410 to GlobalFuturesEngine. _is_china_futures moved into a shared
  _market_hooks module with a case-normalized product table and a
  non-CN exchange guard. 9 new regression cases (RB2410 / rb2410 /
  RB2410.SHFE / CL.NYMEX / M2412.CBOT / CF2412.ICE / AU2412.COMEX /
  FG2412.EUREX / bare CN-collision still routes Chinese).
- Session FTS5 indexes now persist timestamps so cross-session search
  can sort by date. Same path also fixes a re-upsert that was
  wall-clocking every session's started_at on title-rename.
- Vite dev-mode proxy gained the missing /alpha entry; AlphaZoo page
  resolves on npm run dev.

Cleanup
- Deleted swarm/api_models.py + swarm/mailbox.py + SwarmDashboard.tsx
  (dead modules / orphaned UI after swarm-via-agent flow).
- Deleted SessionService.resume_attempt + get_attempts + get_attempt
  + SessionStore.list_attempts + Attempt.mark_waiting_user chain
  (half-baked human-in-the-loop from initial commit).
- Deleted ChatLLM.achat (async sibling never called) and
  _extract_balanced_json (only-tested helper).
- Deleted american_exercise_value in options_portfolio.py
  (driver inlines bs_price logic).
- Deleted _compute_ic_series_reference in factor_analysis_core.py
  (kept-as-reference, no callers).
- Collapsed MEMORY_TYPES duplication between cli.py and
  memory.persistent; restored set-equality invariant assert.
- Deleted runSwarm (~197 LOC), swarmDash state, swarmCancelRef
  write-only ref (orphaned by new agent-driven swarm flow).
- Dropped 76 unused 'vw = vwap(...)' boilerplate lines across
  gtja191 alphas + their now-unused vwap imports.
- Cleared 11 F401 unused-import sites in tools/, agent/, providers/.

Test infrastructure
- test_e2e_harness_v2.py (real-LLM e2e) gated behind
  VIBE_TRADING_RUN_LIVE_E2E so CI no longer flips shape based on
  env-key presence. CI workflow now has explicit --ignore.
- test_auto_compact_with_long_history: monkeypatch TOKEN_THRESHOLD so
  the test deterministically triggers compact instead of relying on
  unrelated LLM behaviour.
- test_compact_tool_explicit: assertion corrected — compact tool is
  special-cased in loop._process_tool_calls and does NOT emit
  tool_call events. Assert on the 'compact' event instead.
- TestSwarmE2E: per-test @pytest.mark.timeout overrides matching the
  internal 300s poll deadlines; cancel test now polls for terminal
  state instead of fixed sleep(3).

Tooling
- ruff per-file-ignores for factors/zoo/** (3783 → 0 F401 noise);
  F841 stays active in zoo to catch real formula bugs.
- frontend tsconfig: noUnusedLocals + noUnusedParameters as
  regression guards.
- highlight.js hoisted to direct dependency (was transitive via
  rehype-highlight).
- Vite proxy: dropped unused /run, /skills, /health, /api, /system;
  added /alpha.

Annotations
- direction parameter renamed to _direction in 6 calc_commission
  signatures (reserved for future borrow-fee asymmetry).
- _MARGIN_PER_CONTRACT in global_futures.py annotated as reference
  data.
- Backtest engines' _calc_pct_change variants cross-reference each
  other in docstrings.

Docs
- 2026-05-18 news entry added to README.md / README_zh.md /
  README_ja.md / README_ko.md / README_ar.md; 2026-05-15 entry
  rolled into the Earlier news <details>.
- Contributors heading: v0.1.7 cycle → v0.1.8 cycle in all 5
  READMEs.

126 files changed, +363 / -1276.
2026-05-18 15:16:24 +08:00
Teerapat Vatpitak 5684d5fe09 fix(providers): log resolved dotenv source
Emit a redacted dotenv resolution diagnostic and redact logged base URLs to avoid leaking credentials.
2026-05-17 23:36:33 +08:00