Requesty (added in #474) is an OpenAI-compatible gateway whose capabilities
were a copy of OpenRouter's: same provider/model naming, same
extra_body.reasoning opt-in, no adapter code of its own. Built-in slots are
kept for providers with their own behaviour; a gateway that speaks an
existing wire format is configured through that provider, and #1532 (API
Route) is being closed on the same grounds.
Migration, identical request shape: LANGCHAIN_PROVIDER=openrouter,
OPENROUTER_BASE_URL=https://router.requesty.ai/v1, OPENROUTER_API_KEY=<the
Requesty key>. Past news and contributor credits in the READMEs are left
as history; the supported-provider lists drop it.
OpenCode Go (https://opencode.ai/zen/go/v1) rejects every chat completion
that lacks a stable per-conversation `x-opencode-session` header:
400 MissingSessionID: Request is missing x-opencode-session and cannot
be routed efficiently.
The `opencode-go` / `opencode-zen` names (#444) only map env variables;
nothing set the header and neither name is in the settings catalog, so
the only way to reach OpenCode was to re-point another provider at its
URL, which then failed as above.
- Add a canonical `opencode` provider (OPENCODE_API_KEY / OPENCODE_BASE_URL,
default Go root, Zen offered as a base_url option) to the settings
catalog, capabilities, swarm allowlist and both CLI onboarding surfaces.
`opencode-go` / `opencode-zen` are unchanged.
- New `src/providers/session_context.py`: a ContextVar carrying the active
Vibe session id. `AgentLoop.run()` binds it for the whole run.
- `ChatOpenAIWithReasoning` injects `x-opencode-session` when the provider
is `opencode*` or the base-URL host is opencode.ai (so a `deepseek`
label pointed at OpenCode also works). The value is the bound session
id, or a per-adapter UUID outside a session; a caller-supplied header
wins. The `Vibe-Trading/<version>` User-Agent identifies the client as
OpenCode's usage policy asks.
- Tests: registration, host detection, ContextVar scoping and wire-level
header checks via httpx.MockTransport. README and .env.example updated.
Verified against the live Go endpoint: preflight reports `LLM (opencode)
OK` and a chat session completes where it previously failed every time.
Signed-off-by: Antonio <tonydu@gmail.com>
The incoming change closed the create-then-chmod permission window by creating
the file 0600 with O_EXCL, but it unlinked the previous partial first, so any
failure between the unlink and a completed write left the user with no recovery
file at all — the one thing the file exists to provide.
Use the mkstemp + replace pattern _finalize already uses 19 lines below: the
temp file is 0600 from creation, the swap is atomic, and a failed write leaves
the previous partial untouched. The section header above already described this
helper as an atomic partial save; now it is one.
_save_partial wrote the collected values with Path.write_text and narrowed the
file with chmod afterwards. write_text creates using 0o666 & ~umask, so the API
keys were on disk readable by other local users until the chmod landed, and
permanently whenever it raised, since the error was discarded.
This follows conventions already established elsewhere in the repo rather than
introducing a new one. _finalize writes the final .env through mkstemp, which
creates at 0600, and _cache_hmac_key in tools/alpha_bench_tool.py uses
os.open with O_EXCL and mode 0600, noting that a concurrent writer must not be
able to race it into a world readable key. _save_partial, the crash resilience
path, was the one place that did not.
Measured on Linux under the default umask: the file existed at 0644 holding the
keys before being narrowed to 0600, and at 0600 from creation afterwards.
A leftover .env.partial from an interrupted run is removed rather than
truncated, because O_CREAT does not apply the mode to a file that already
exists and the old permissions would be inherited silently.
Signed-off-by: lukiod <mohakgupta0981@gmail.com>
Self-fix on merge: moved langchain-anthropic from core dependencies to an
[anthropic] optional extra (like [deepseek]) — _build_anthropic lazy-imports it
and raises an actionable install hint, so the base install stays lean. Resolved
llm.py conflicts against #563's centralized get_llm_credentials + the anthropic
native adapter-mode branch. Closes#694.
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).