* chore(release): 0.1.10 — version sync, wheel packaging, Docker weasyprint libs
Release-prep for 0.1.10 (bundles the global data layer + everything since 0.1.9):
- version 0.1.9 -> 0.1.10 synced across all surfaces: pyproject, frontend
package.json/lock, sidebar footer + i18n, SKILL.md, Dockerfile LABEL. REST
API + MCP server now derive the version from cli._version (were hardcoded
"5.0.0" / unset).
- pyproject package-data ships skill example scripts (skills/**/*.py) so the
wheel + sdist include them; fastmcp floor -> 2.14.0 (first release where the
MCP version= kwarg applies).
- Dockerfile installs weasyprint native libs (Pango/HarfBuzz/Fontconfig/Cairo/
gdk-pixbuf + DejaVu) so PDF reports render in the image; .dockerignore *.md
-> /*.md (root-anchored, skill markdown still ships).
- README news (5 locales): 06-19 reframed as the v0.1.10 release; 06-19/06-18
trimmed.
Verified: clean-room pip install imports all new loaders/tools; wheel + sdist
carry the eastmoney/sec-edgar skills incl. UTF-8 references + scripts; 4167
backend tests pass (weasyprint PDF path covered on CI).
* docs(release): credit v0.1.10 cycle contributors across READMEs
Refresh the Contributors section in all 5 READMEs from the v0.1.9 cycle to
the v0.1.10 cycle (PRs #162–#260): @Hinotoi-agent, @mvanhorn, @gyx09212214-prog,
@BillDin, @Robin1987China, @LemonCANDY42, @zwrong, @forge-builder, @skloxo,
@LeeCQiang, @KaiLuettmann, @ngoanpv, @ShahNewazKhan, @sambazhu, @bhlt, @octo-patch.
Completes work the 2026-06-18 self-merged batch left half-applied:
- Tighten the mandate proposal_id relay/preview parsers in api_server.py
and cli/_legacy.py to the strict mp_[0-9a-f]{32} format already enforced
by commit.py (#256), so the relay and the commit gate agree on format.
- Document the MCP initTimeout config key (shipped in #263) in README and
wire its base-default passthrough in config/loader.py.
- Add the registered pytest.mark.unit marker to test_mandate_commit_security.py.
#260 Research Autopilot shipped two reproduced runtime-breaking bugs:
- run_research_autopilot never received the host session_id (it read only
kwargs, which the LLM cannot know), so every call errored 'session_id is
required'. Inject default_session_id via build_registry like the goal tools.
- generate_backtest_config returned a run_dir under ~/.vibe-trading/runs,
which safe_run_dir rejected, breaking the advertised write_file -> backtest
handoff. Add ~/.vibe-trading/runs to the allowed run roots.
Also validate the derived backtest source against VALID_SOURCES (fall back to
'auto' with a warning) and document the goal-replace semantics.
#252 local loader silently emptied tz-aware CSV/Parquet inputs (a tz-naive vs
tz-aware Timestamp comparison raised TypeError that was swallowed). Normalize
the timezone in _normalize_columns.
Adds registry-level + end-to-end tests for both autopilot tools and a tz
regression test for the loader.
Adds an 'Official channels & impersonation' section pointing to the only
official Discord (HKUDS server) and warning that any wallet 'verification'
prompt is a scam, after the 2026-06-18 impostor-Discord incident.
Re-add the Discord badge (5 READMEs) and an issue-template contact removed
in 8346def, now pointing at the org-controlled HKUDS community server
(discord.gg/6TdQnT5xcF, guild 1296348098003734629, ~1.9k members) — the same
server linked from the HKUDS org profile and LightRAG — instead of the
compromised invite. Keeps GitHub Discussions as a second contact.
The discord.gg/2vDYc2w5 invite (in all 5 READMEs and the issue-template
contact links) resolves to a "Vibe-Trading" Discord server we no longer
control. It runs a fake Collab.Land wallet "verification" via
collab.layers-land.com — a throwaway domain registered 2026-06-18 — that
phishes users' crypto wallets. Remove every public pointer to it and replace
the issue-template community link with GitHub Discussions.
* feat(autopilot): add goal-hypothesis bridge for Research Autopilot Phase 1
New `run_research_autopilot` agent tool that connects the Hypothesis
Registry to the Research Goal runtime. Given a hypothesis_id, the tool
reads the hypothesis, creates a research goal with the thesis as
objective and backtest-relevant criteria, and returns a goal snapshot
with next-step hints.
Auto-registered via BaseTool subclass discovery.
Signed-off-by: Robin1987China <fanrenzhige@163.com>
* feat(autopilot): add generate_backtest_config tool for Phase 2
New `generate_backtest_config` tool auto-generates backtest config.json
from hypothesis metadata. Maps universe names (CSI 300, S&P 500, etc.)
to standard index codes and uses hypothesis.data_sources for the source
field. Writes config.json to a run directory and returns the path so the
agent can proceed to create signal_engine.py and call backtest.
Signed-off-by: Robin1987China <fanrenzhige@163.com>
* fix: harden research autopilot config generation
---------
Signed-off-by: Robin1987China <fanrenzhige@163.com>
Co-authored-by: Robin1987China <fanrenzhige@163.com>
Co-authored-by: Haozhe Wu <haozhe_wu@connect.hku.hk>
* feat(loaders): add local data loader for CSV/Parquet/DuckDB files
- New 'local' loader reads OHLCV from user-configured CSV/Parquet/DuckDB sources
- Config file at ~/.vibe-trading/data-bridge/config.yaml maps symbols to files
- Symbols prefixed with 'local:' auto-route to the local data source
- Integrated into loader registry with fallback chain support
Signed-off-by: Robin1987China <fanrenzhige@163.com>
* fix: allow duckdb local loader without path
---------
Signed-off-by: Robin1987China <fanrenzhige@163.com>
Co-authored-by: Robin1987China <fanrenzhige@163.com>
Co-authored-by: Haozhe Wu <haozhe_wu@connect.hku.hk>
- Replace assistant-role handoff messages with user-role in agent loop
to avoid "assistant message prefill" rejection on Anthropic Claude Opus 4.8+
- Allow MOONSHOT_USER_AGENT env var to override Kimi/Moonshot User-Agent header
for compatibility with Kimi Coding API endpoint
Closes#246, Closes#204
Signed-off-by: wesley <wesley87@wesley87deMacBook-Pro.local>
Co-authored-by: wesley <wesley87@wesley87deMacBook-Pro.local>
Add a first-class `glm` alias of the existing `zhipu` provider so a model
name like `glm-4.6`/`glm-5.1` resolves to the Zhipu capability record and
base URL instead of silently falling back to the OpenAI slot.
- capabilities.py: share one `_ZHIPU_CAPABILITIES` record between `zhipu`
and the new `glm` alias (mirrors kimi/moonshot), and infer `zhipu` for
model ids starting with `glm`.
- llm_providers.json: register a `glm` entry aliasing the Zhipu env names
and default model so onboarding/diagnostics surface it.
- tests: cover the alias, model-name inference, env-name resolution, the
unchanged openai fallback, and the new registry default.
Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Require a valid bearer token (not just loopback peer IP) for settings WRITE endpoints (/settings/llm, /settings/data-sources) when API_AUTH_KEY is configured, closing a DNS-rebinding / loopback-trust bypass while keeping zero-config local dev writes.
Also reconciles the DNS-rebound security tests across the #241-#245 sprint: now that #242's rebound-host middleware 403s loopback requests with an attacker-controlled Host before they reach the route, the end-to-end rebound tests for /swarm/runs and /sessions/messages (#243) and /settings/llm (#245) assert the request is rejected at the host-gate layer (403) and never reaches the shell-tool / settings-write decision. Full suite: 3217 passed.
Symptom 2 of #229: clicking Stop during a run did nothing, or only took
effect after a long delay. The cancel signal (_cancelled bool) was polled
only at the top of the ReAct iteration loop, so a Stop pressed while the
agent was mid-LLM-stream or executing tools was ignored until the entire
current iteration — the whole streamed answer plus all its tool calls —
finished and the next iteration check ran.
Add cooperative cancel checkpoints at the granularity the user actually
waits on:
- Switch _cancelled to a threading.Event (_cancel_event) so the flag is
safe to poll from the streaming thread.
- Thread a should_cancel predicate into ChatLLM.stream_chat; the per-chunk
loop breaks as soon as it is set, so a live stream stops at the next
token instead of running to completion.
- After the stream returns, check the flag before processing tool calls:
a cancel during streaming ends the run immediately and discards the
partial turn (no tools run).
- Check the flag at the top of _process_tool_calls and between tool batches
in _batch_execute, so a multi-tool turn stops after the current tool
rather than launching the rest.
Limitation (documented in the PR): a connection that hangs before emitting
any chunk leaves the worker blocked in a socket read, which Python cannot
interrupt cooperatively from another thread; that case stays bounded by the
existing client timeout (TIMEOUT_SECONDS). Cancel now lands promptly in all
the normal "working" states (tokens streaming, tools running).
Tests: chat-level should_cancel breaks the chunk loop (and absence is a
no-op); loop-level cancel mid-stream ends the run as cancelled and skips
tool execution. Full suite: 3199 passed, 2 skipped.
Symptom 1 of #229: navigating away from the chat page and back often made
the agent "stop working". The page run is server-side and the frontend only
subscribes via SSE; the unmount cleanup tore down that stream, but the
navigation effect only (re)subscribed when the URL session differed from the
store session. Returning to the SAME session hit neither branch, so SSE was
never reopened — in-flight events stopped arriving and the run looked frozen
until the 90s safety timeout flipped it to idle.
The backend already buffers events per session and replays them on connect
(GET /sessions/{id}/events?replay=active), and setupSSE already requests it;
the only defect was the missing re-subscribe on same-session remount.
Add that branch: re-hydrate like a reload — reset the transient streaming
view first (so replay=active rebuilds the in-flight turn from the ring buffer
instead of duplicating deltas onto the preserved text), refresh committed
history (covers an attempt that finished while away), then re-subscribe.
Symptom 2 (Stop ineffective during a hung/long turn) is cooperative-cancel
granularity in the agent core and is tracked separately.
Verified: tsc clean, frontend build passes, existing store/useSSE/Runtime
tests green (42).
GitHub forces Node 20 actions onto the Node 24 runtime starting 2026-06-16
and removes Node 20 from runners on 2026-09-16. Bump the three first-party
actions to their latest Node-24-native majors across all workflows:
- actions/checkout v4 -> v6
- actions/setup-python v5 -> v6
- actions/setup-node v4 -> v6
Inputs are unchanged (node-version "20" is the frontend JS runtime, not the
action runtime). Third-party cloudflare/wrangler-action is out of scope.
web_search relied on ddgs's default 'auto' backend, which leads with
DuckDuckGo — heavily rate-limited from cloud/shared IPs. When DDG
threw, the whole tool returned ❌ (issue #231) even though Google/
Bing/Brave/Mojeek/Yahoo were reachable.
- Pass an explicit ordered backend list so ddgs falls through a
throttled engine to the next; override via VIBE_TRADING_SEARCH_BACKENDS
- Retry transient failures with linear backoff (3 attempts)
- Treat "No results found" as an ok+empty envelope, not an error
- On persistent failure, return an actionable message (retry / set
backend env / use read_url) instead of a bare exception string
- Report the backend list used in the ok envelope
Tests: new tests/test_web_search_tool.py (6 cases, mocked ddgs) cover
multi-backend pass-through, env override, retry-then-succeed,
no-results, persistent-failure message, and max_results cap.
The baostock loader previously only accepted tushare-style codes (601398.SH)
and silently rejected baostock-native codes (sh.601398) — the format baostock
uses in its own docs. _is_a_share() now accepts both (case-insensitive), and
_fetch_one() passes native codes through unchanged while still converting
tushare-style suffixes. Adds 10 unit tests.
Thanks to @bhlt for the fix.
Malformed values like CCXT_TIMEOUT_MS=abc / OKX_TIMEOUT_S=abc raised ValueError at import time and could block startup. Add shared positive_env_int/float helpers in loaders/base.py; invalid/blank/non-positive timeout/budget values now warn and fall back to defaults. Valid values still honored.
Closes#227.
yfinance download(end=...) is exclusive while the loader API treats end_date as inclusive, so the final trading day was dropped. Pass end_date + 1 day to the yfinance call (bulk + single-symbol fallback); cache lookups/writes keep the original requested range.
Closes#226.
Add opt-in chart payload modes for the Run Detail endpoint and UI. Default /runs/{id} stays byte-for-byte unchanged; ?chart_payload=summary returns metadata + chart_symbols (no bars/markers); ?chart_symbol=SYM returns one symbol's bars+markers. Run Detail UI renders summary first, then progressively loads selected symbols with loading/progress + cancel. New copy localized en + zh-CN.
Refs #215.
Persist provider-reported AgentLoop usage as a run-scoped artifact (<run_dir>/llm_usage.json) and surface it additively on /runs/{id}. Provider-reported tokens only — no prompt/content capture, no price estimation. Behind the existing usage guard so providers without usage_metadata write nothing. Live llm_usage SSE event keys unchanged.
Refs #214.
Builds on @Shizoqua's verified provider core (#201) and resolves the three
open-box blockers from review:
- No built-in feed catalogue: the shipped DEFAULT_FEEDS routes (/stock/notice,
/stock/news) don't exist in RSSHub, so a default run scored every bar 0.0.
Feeds are now declared explicitly in config (event_feeds = list of
{name, route_template, event_type, code_style}); DEFAULT_FEEDS is empty, so
there is nothing to silently rot as RSSHub routes evolve.
- Per-symbol code formatting: a feed declares a code_style
(raw / bare / exchange_prefix), so dotted backtest codes (600519.SH) are
rewritten to the form a route needs (e.g. SH600519 for xueqiu-style routes).
- No silent all-zero scores: each fetch failure is logged per feed/code, and
query_events raises EventProviderError when every fetch failed (configured
but fully unreachable) instead of returning an all-zero event_score that
reads as "sentiment considered, no signal". A reachable feed that returned
no items stays a legitimate empty result.
PIT discipline, defusedxml hardening, the pluggable scorer, and the
engine/runner wiring are otherwise unchanged. Adds 12 tests for the code
styles, config parsing/validation, and the loud-failure path.
Thanks @Shizoqua (#201) for the provider core.
Co-authored-by: Lanre Shittu <136805224+Shizoqua@users.noreply.github.com>
A-share data sources (free, no-auth, bypass eastmoney CDN IP blocks):
- baostock loader (TCP protocol) + tencent loader (HTTP), both wrapped with
cached_loader_fetch like the mootdx/akshare loaders so they honor the opt-in
local cache
- registered into the a_share fallback chain (tushare -> mootdx -> baostock ->
tencent -> akshare); auto-detect default source stays tushare
- pyproject [ashare] extra for the optional baostock dependency
Frontend i18n (English default, sidebar toggle to Chinese, persisted):
- i18next + react-i18next + browser-languagedetector; first visit defaults to
English (localStorage-only detection, no navigator sniff), the toggle persists
the choice across reloads
- en / zh-CN locales at full key parity, including the Runtime page
- i18n initialized in the test setup so useTranslation resolves real strings;
restored the exported METRIC_LABELS so formatters consumers/tests keep working
Thanks @skloxo (#217) for the i18n layer and the A-share data sources.
Co-authored-by: skloxo <skloxo@users.noreply.github.com>
Exit interactive CLI shows the session ID so users can copy-paste
`vibe-trading resume <id>` to resume later. The resume subcommand
loads a specific session by ID and enters the interactive loop with
full history — messages are persisted through the existing
_run_one_turn / _append_message path.
Signed-off-by: zwrong <1003912034@qq.com>