Days 234–249. Three defaults changed, and one of them can break a setup that worked. MCP stdio servers and shell hooks now start with your provider API keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, …) unset, so a server or hook that silently relied on an inherited key will fail its own calls until you give it the key explicitly or set YOYO_SUBPROCESS_ENV_SCRUB=0. With no --model, --provider openrouter now runs anthropic/claude-sonnet-5.5 (was anthropic/claude-sonnet-4-6) and --provider google runs gemini-3.8-flash (was gemini-2.5-flash). And yoyo now refuses what it used to ignore: an unknown flag, a mistyped --provider, or a numeric flag whose value doesn't parse exits 2 before anything is sent. Those are why this is a minor bump. The rest of the span is a data-loss guard on write_file, the GPT-6 openai default finally able to call tools, MCP servers that die (or survive Ctrl+C) honestly, and network errors that fail fast instead of retrying for a minute.
Changed (defaults you will notice)
- Provider keys are unset for MCP stdio servers and shell hooks. A server from
--mcpor[mcp_servers.*](yours or a repo's.yoyo.toml), and every[hooks]command, starts without the provider credential variables;GITHUB_TOKEN/GH_TOKENare kept. To give a server a key on purpose, put it in[mcp_servers.<name>] env = { KEY = "..." }; to turn the scrub off,YOYO_SUBPROCESS_ENV_SCRUB=0(orfalse; any other value, including a typo, keeps it on). The model's bash tool keeps the scrub opt-in (YOYO_SUBPROCESS_ENV_SCRUB=1);/run,/bgand/watchare not scrubbed. @yuanhao decided the MCP half on #1064 (#1064, hooks Day 238, MCP Day 248). - openrouter default:
anthropic/claude-sonnet-5.5, a dotted id OpenRouter actually lists (it has no dashed Anthropic ids). Everyanthropic/…id on openrouter now gets the Anthropic preset's window, output limit and price instead of 128K / 4096 tokens (#1037). If you relied on the old default, pass--model anthropic/claude-sonnet-4.6, which also resolves to its preset now. - google default:
gemini-3.8-flash(wasgemini-2.5-flash, which had no preset and ran on guessed limits);gemini-3.0-pro/flash, absent from Google's model page, left the known list (#1021).GEMINI_API_KEYalone now starts yoyo on google. - Unknown or malformed flags are refused, exit 2, nothing sent. An unknown top-level flag names the nearest real one (
yoyo --modle claude-haiku-4-5 -p hi→unknown flag '--modle' (did you mean --model?)); before, it printed a warning, ignored the flag and sent the request on the default model, andyoyo log --bogussent "log" as a prompt and exited 0. Subcommand flags (yoyo diff --stat), anything after--, and a quoted prompt that starts with-are unchanged (#1109). A top-level--now ends flag parsing and every word after it reaches the prompt verbatim (#1110, #1112, #1113, #1114). Also refused: a mistyped--provider(did-you-mean; before, it sent the fallback key tolocalhost:8080for 80 s, #1103),--max-tokens abc/--temperature hot/--max-turns x/--context-window big, and a global flag placed before a subcommand (which used to turn the subcommand into a paid prompt). - On
--provider openai, the GPT-6 preset ids (gpt-6-sol— the openai default —gpt-6-luna,gpt-6-astra,gpt-6.1-sol) now go over OpenAI's Responses API (/v1/responses) instead of Chat Completions, so tool calling works on yoyo's default openai model. OpenAI documents that Chat Completions does not support function calling with GPT-6 Astra or GPT-6.1 Sol, and allows it for Sol and Luna only atreasoning_effort: none, which yoyo never sent. Every other openai id (gpt-4o,gpt-5, …) still uses Chat Completions. If you point--provider openaiwith a GPT-6 id at a third-party--base-url, that endpoint now needs/responses. Checked against a local stub server, not against a real OpenAI call. The GPT-6 sub-agent,/sideand architect doors use the same wire.
Added
write_filerefuses to overwrite an existing file the session has only partly read (read_file pages at 500 lines; writing "the whole file" back after page 1 silently deleted the rest). The refusal names the lines read and the two moves: read the rest withoffset/limit, oredit_filein place. A page that output truncation cut in the middle no longer counts as read (#1094).- Opt-in write fence:
YOYO_BLOCK_OUTSIDE_WRITES=1refuseswrite_file/edit_fileoutside the launch directory, in every mode including--yes. - An instruction file (CLAUDE.md, YOYO.md, …) over 150,000 bytes gets a loud stderr warning naming the file, its size and its token estimate (#1070).
- When a stdio MCP server dies mid-session its tools are withdrawn at the next turn, and the model (once) and you are told which server went away, instead of the model calling dead tools (#1041).
/cost,/tokens,--output-format jsonand the--cost-warntally count delegated sub-agent spend, on its own line beside the per-turn breakdown (#1073, #1108).
Fixed
- Argv. The shell verbs refuse an unknown dash-led word instead of using it as an operand or ignoring it (exit 2, did-you-mean, nothing runs):
changelog,memories,docs,def,find,outline,grep(#1111),commit(--ammendno longer goes into the commit message),review(a mistyped flag is no longer read as a file path or PR number, #1129;yoyo review -- --quickreviews the path--quick, #1130), and the verbs that take no flags. yoyo diffand/diffrefuse a flag they do not know (yoyo diff --cahced) with the usage line andunknown flag: --cahced, exit 2, instead of dropping it and showing the unstaged + staged diff with exit 0.--is accepted as a path separator (#1107).- Shell wording.
yoyo grep,yoyo def,yoyo outline(#1127, partial),yoyo goal,yoyo todoandyoyo watchprint their shell form, not the REPL's/verb; bareyoyo runnow printsusage: yoyo run <command>instead of the REPL-onlyusage: /run <command> or !<command>(neither/runnor!works at a shell prompt). The same goes foryoyo find,yoyo tree(its usage line and its not-a-git-repository message) andyoyo undo. The REPL's/run,/find,/treeand/undotext is unchanged. Part of #1086; other shell verbs still print their slash form.yoyo doctor's rows and footer give advice that works on the surface they are printed on (#1074, #1075, #1083), and its project-context row names instruction files the fence refused (#1045). - Network. A DNS failure on an RFC 6761 reserved name (
.invalid,.test,.example,.localhost) stops retrying (#1120); a refused connection to a loopback server fails in seconds and names the URL instead of 70 s of retries;-pnetwork errors print a one-line summary without the REPL-only/retryadvice (#1115). The 401 hint names where the key actually came from (--api-key, config, alias env var) (#1062), and a key passed with--api-keyskips the first-run setup wizard. - Processes.
/bg killand/quitstop a job's whole process group with SIGTERM, a 10 s grace (docker stop's default), then SIGKILL, and report what actually happened (#1077, #1080, #1081)./run,!,/bg run,/watchand the auto-check edit hook stop waiting on a backgrounded grandchild that holds the output pipe (#1063). Stdio MCP servers start in their own process group, so Ctrl+C no longer kills them (#1098), and every live server is closed on exit, bounded so a hung one can't block it (#1099). A bash command that times out returns the output it printed so far./spawnno longer force-removes a worktree holding uncommitted work and its stale sweep skips worktrees whose worker still runs (#1057, #1097). - Deny list and bash guard. With a deny list set,
eval "$(...)"of a runtime-assembled command is refused (#1011), excepteval "$(<known env emitter> …)"such asssh-agentwhen the inner command passes the deny check (#1066). The rm-root guard tracks quotes across lines, so a quoted newline no longer splits a mention into a command (#1068, #1101); guard refusals no longer get a "try a simpler version" hint or a whole-prompt auto-retry. yoyo astandyoyo mapdetect ast-grep only when the binary really is ast-grep; on Debian/Ubuntu/usr/bin/sgis util-linux's set-group command, which passed the old--versioncheck (#1131). Shellyoyo astexits nonzero when ast-grep is missing or the search errors.--output-format json/stream-jsonescape bidi, zero-width, C1 and DEL characters as\uXXXX(#1092).- Auto-memory keeps the newest entries instead of freezing at its cap, stops saving content-free panic headers, and stored content-free entries no longer reach the system prompt (#1106).
- A Ctrl-C'd tool call reaches the model as "cancelled", with no failure hint (#1061).
yoyo undo --last-commit/--allexit 1 when git refuses or outside a repository, and--allnames what it could not revert (#1058, #1059, #1065)./pr createon a detached HEAD says so (#1072)./grepfinds matches in a single file outside a git repo and searches each of several paths (#1087, #1091). Bareyoyo run,tree/undousage errors exit 2 (#982). - The no-config bedrock default
anthropic.claude-sonnet-4-6runs on Anthropic's published limits, now from yoagent 0.26 presets (#1116, #1123). - Images you attach yourself (
--image,/add,@path) are now refused before reading if they are over 20 MB, the same limit (and the same "exactly 20 MB is allowed") as the agent's ownread_file. Before, the whole file was loaded and base64-encoded: a 300 MB--imageused 4.7 GB of memory, then the provider rejected the request anyway. - Price table: exact rows for Google (gemini 3.x flash/pro, introductory rates billed by date, the >200K-prompt tier for gemini-3.1-pro-preview), xAI (by the endpoint yoyo calls), Mistral and Z.ai GLM, each checked against the vendor's pricing page (#1012, still open).
- yoagent 0.25.10 → 0.26.8.
Price table audit
price_drift_audit(DeepSeek rows): passed (deepseek-v4-prois a registered divergence;deepseek-r1/deepseek-v3are not listed by models.dev, so nothing audits them).- General sweep (run 2026-11-04):
price audit: SUMMARY compared 114, matched 78, drifted 23, cache_read_only 13, unpriced 61 of 175 catalogue rows (rel_tol 1%). The 23 drifted rows are UNRECONCILED and owned by #1012 (73 at v0.3.0). By provider: OpenAI 16 (gpt-4o-2024-05-13 and the gpt-5.x family billed at a shared 1.25/10 arm), Z.ai 5, DeepSeek 1, Mistral 1./costfigures for those models may be wrong. The 13cache_read_onlyrows are the known unmodelled-cache gap.