100 Commits
Author SHA1 Message Date
Pranit 9b558ed785 Merge pull request #1477 from vercel-labs/fazxes/fuzzy-model-search
Make model search fuzzy across provider and model names
2026-10-27 22:09:01 -04:00
Pranit 63540de785 Merge pull request #1414 from vercel-labs/fazxes/consolidate-provider-and-session-code
Consolidate duplicated provider and session code
2026-10-27 21:14:26 -04:00
Pranit f9ddaff828 Make model search fuzzy across provider and model names
- The model menu and the inline `/model <query>` suggestions match each query word against the full `provider/model` ID, so `anthropic opus` and `claude sonnet 4.5` find their models.
- Punctuation inside a query word is ignored, so `sonnet45` finds `claude-sonnet-4.5` and `z.ai glm` finds the `zai/glm-*` models.
- A query word also matches when its letters appear in order from the start of a word in the ID, so `cs45` finds `anthropic/claude-sonnet-4.5`.
- Results that contain the query as typed come first, then word matches, then fuzzy matches, each in catalog order, so queries that already worked keep the same top results.
2026-10-27 21:01:23 -04:00
Pranit 7f2383af70 Merge pull request #1471 from vercel-labs/prepare-v0.0.15-refresh-20261027
Prepare v0.0.15 release
2026-10-27 14:17:34 -04:00
Pranit ec872b150f Prepare v0.0.15 release 2026-10-27 14:09:55 -04:00
Pranit 404b5ed7b7 Merge pull request #1466 from vercel-labs/fazxes/run-mcp-tools-across-turns
Run MCP tools across turns and recover from empty cut-off replies
2026-10-27 13:53:54 -04:00
Pranit 8122faa1d2 Keep the empty-reply retry inside known limits
The retry of an empty reply cut short by the provider's default used a
flat 32,768-token limit and kept it for every later step, even when the
catalog named a limit of its own. A known context window now bounds the
limit, as it bounds a full-window limit, and a catalog limit wins on
later steps.

ChatGPT's Codex endpoint chooses its own output limit, so a retry there
sent the same request again and said it was raising the limit. Providers
now say whether a request carries fx's limit, and the retry runs only
through one that does.

The model cache and command replay comments now describe the bounded
wait and the run id in each handle.
2026-10-27 12:56:05 -04:00
Pranit 50c641e6e3 Merge main into the MCP tool fixes
Main now pluralizes subagent counts in tool group summaries the same way
this branch pluralizes MCP calls, so the label keeps both: "subagents"
and "MCP calls".
2026-10-27 12:21:35 -04:00
Pranit d521b9aa84 Merge pull request #1467 from vercel-labs/fazxes/pluralize-subagent-summary
Pluralize subagent counts in tool group summaries
2026-10-27 12:18:52 -04:00
Pranit e874c63a36 Update tests and the design record for the new handle, label, and retry
Tests that read command replays matched the old handle, fx-command-replay-
and the hash, so they found none once each run's handle gained a run id.
They now accept the run id and take the blob's name from the hash after
it. D91 in the session manager's design record amends D44 with the new
handle shape.

A name the registry lacks but that has the mcp_ prefix now counts as an
MCP call, so the unsupported-tool test expects "1 command · 1 MCP call".
The ACP length test scripts the retry's reply and checks that the retry
carries a 32,768-token limit and never asks for a summary.
2026-10-27 11:42:45 -04:00
Pranit 8625c8f600 Pluralize subagent counts in tool group summaries
A tool group with several subagent calls now reads "4 subagents"
instead of "4 subagent". A group with one subagent call still reads
"1 subagent".
2026-10-27 11:20:30 -04:00
Pranit 4f5153c8c4 Give each command run its own output handle
A command's full_output_handle named the blob that holds its output, so
two commands with identical output got the same handle, and a later
result looked like a replay of an earlier one.

The handle now starts with a random id for the run and still ends with
the blob's hash. Runs with the same output keep sharing one stored copy
under handles of their own, and existing handles still resolve.
2026-10-27 11:13:33 -04:00
Pranit bedc0e7cf3 Ask again once when a reply runs out of output before any text
When a request named no output limit and the provider's own default ran
out before any text or tool call, fx ended the turn with "Done." for an
empty reply.

fx now sends the request again once, with a limit of 32,768 tokens, and
says it is retrying. If that reply is also empty, or the request already
had the catalog's limit, the turn ends with "The reply ran out of output
tokens before any text." instead of "Done.".
2026-10-27 11:13:22 -04:00
Pranit 65846154c9 Wait briefly for a failed model catalog's retry
When the model catalog failed to load, the retry a submitted prompt
starts ran behind the failed state, so the prompt's first request went
out without the model's limits. With no output limit, some providers
stop a reply at 4,096 tokens.

A request now waits up to two seconds for that retry, as it waits for a
first load. A retry that lands in time gives the request the catalog's
limits. A slower one leaves the request as it was before, so a retry
that hangs never holds a prompt for longer than that.
2026-10-27 11:13:01 -04:00
Pranit 0fb5cb8e6f Run MCP tools the model calls without loading them first
A tool the model loads with capability_search or mcp_select_tool stays
loaded only for that turn. When the model called it again in a later
turn, or after a resume, the terminal app and fx ask failed the call
with "Dynamic MCP tool not selected for this model step", and the model
had to load the tool again before it could use it.

ACP already loads such a call's current definition from the live
catalog and sends the call through normal validation and permission
review. The terminal app and fx ask now do the same. A name that no live
server has still fails without reaching any server.
2026-10-27 11:12:53 -04:00
Pranit 1ce845071e Label MCP tool calls in tool group summaries
A tool the registry does not hold was counted as a command, so a turn's
MCP calls showed up as "2 commands" in its tool group summary and as
commands started in the status line.

Every MCP tool name has the mcp_ prefix, so those calls now have their
own activity kind. The summary reads "1 MCP call" or "2 MCP calls", and
the status line counts MCP calls made.
2026-10-27 11:12:46 -04:00
Pranit 70866b2712 Merge pull request #1464 from vercel-labs/fazxes/refresh-pgso-corpus-expectations 2026-10-26 23:26:56 -04:00
Pranit 99b8ad47d7 Refresh PGSO corpus expectations 2026-10-26 18:17:33 -04:00
Pranit e71a663b95 Merge pull request #1462 from vercel-labs/prepare-v0.0.15
Prepare v0.0.15 release
2026-10-26 16:20:02 -04:00
Pranit a13511634f Prepare v0.0.15 release 2026-10-26 16:17:21 -04:00
Pranit de8e919056 Merge pull request #1460 from vercel-labs/esteban/mcp-menu-sign-in-link
Link a waiting sign-in in one line on the server screen
2026-10-26 16:16:02 -04:00
Pranit f0732a02c3 Merge pull request #1457 from vercel-labs/fazxes/reconnect-dropped-mcp-connections
Reconnect when an MCP server drops an idle connection
2026-10-25 22:38:54 -04:00
Pranit a97f9d9e2f Reconnect when an MCP server drops an idle connection
- An MCP tool call over HTTP after the server dropped an idle connection now goes out on a new connection, instead of failing with "the server went away" on every call until fx restarts.
- Renewing an MCP login after the sign-in server dropped an idle connection now goes through, instead of failing with "the sign-in server didn't answer" until fx restarts.
- A connection the server closed cleanly while idle is replaced before the call is sent, so the call no longer fails once with "the tool may have run".
- Connections unused for more than a minute are replaced instead of reused, so a call after the network silently dropped one no longer waits out its timeout.
- A request whose send fails goes out once more on a new connection. A request that was fully sent is still never sent twice.
2026-10-25 21:37:17 -04:00
Pranit a2e11d98f6 Merge pull request #1453 from vercel-labs/fazxes/link-sign-in-addresses
Link sign-in addresses instead of printing them
2026-10-24 23:00:30 -04:00
Pranit 0840784f9e Read the sign-in driver's addresses from their links
The repeated-login driver read the sign-in address the transcript printed.
The transcript now links it, so the driver records the shell's output and
reads each link's target instead.
2026-10-24 20:07:29 -04:00
Pranit 2d61cc038d Link sign-in addresses instead of printing them
A typed /mcp login and the Vercel sign-in picker printed the whole sign-in
address. Both now show a few underlined words whose OSC 8 link is the
address: "Open the sign-in page" for MCP and "Authorize with Vercel", the
way Codex and Grok already link theirs. Notices underline a link on every
row it wraps onto. An address that can't be a link target is still printed
so it can be copied.
2026-10-24 19:25:50 -04:00
Pranit 7d854a42c6 Merge pull request #1452 from vercel-labs/fazxes/effort-menu
Add /effort to choose the reasoning effort
2026-10-24 14:33:52 -04:00
Pranit fefb9d9a51 Merge pull request #1444 from vercel-labs/fazxes/restart-only-changed-mcp-servers
Restart only the MCP servers a config change affects
2026-10-24 14:31:27 -04:00
Pranit 04a5e3af6a Add /effort to choose the reasoning effort
- `/effort` opens a menu of the current model's reasoning effort levels with the one in use marked. ↑↓ moves, Enter applies the highlighted level, and Esc closes it.
- `/effort <level>` applies a level without the menu, and `/effort default` goes back to the model's default.
- Tab after `/effort` opens a level column under the prompt, as Tab after `/model` opens the model column. ↑↓ previews the level in the status line, typing filters the list, Tab fills in the highlighted level, and Enter applies it.
- A new level is saved like a level chosen in `/settings`, and fx prints `Reasoning effort set to <level> for <model>`.
- During a response, the line reads `Next turn will use reasoning effort <level> for <model>`. The running turn keeps its level.
- A model without levels, a level the model doesn't offer, or a malformed level prints a notice and leaves the effort unchanged. If the model list is still loading, the notice asks to try again.
- In a short terminal, the menu leaves the end of the conversation in view and scrolls.
- `/model` still asks for the effort after a model is chosen.
- `/help` lists `/effort` with the model commands.
2026-10-24 13:28:03 -04:00
Pranit 2327e3cb8e Merge pull request #1445 from vercel-labs/fazxes/permissions-panel
Show /permissions in a footer panel
2026-10-23 22:26:36 -04:00
Pranit 5f9aed6634 Merge pull request #1441 from vercel-labs/fazxes/status-session-panel
Show the session ID in a /status panel
2026-10-23 22:26:05 -04:00
Pranit 45abf082ae Merge pull request #1446 from vercel-labs/fazxes/ask-twenty-questions
Allow up to 20 questions in one ask_user_question call
2026-10-23 22:23:12 -04:00
Pranit ea13c33573 Model sign-ins across in-place MCP reloads
The SignIn model and its driver still had every reload replace the host and
end the sign-in. A reload now keeps a sign-in when it leaves that server
alone and ends it when it removes or changes the server; the host swap
remains for reloads the loader can't apply in place. KeptSignInGoesOn checks
the first, with a planted bug for the old behavior.
2026-10-23 20:20:18 -04:00
Pranit 28cf53b548 Allow up to 20 questions in one ask_user_question call
- The model can ask up to 20 questions in one ask_user_question call, so a related set of questions opens as one prompt instead of several prompts of 4.
- The tool schema and description tell the model the new limit.
- A call with more than 20 questions is rejected with "provide 1 to 20 questions", and no prompt opens.
- Resumed sessions show every answer from a batch of up to 20 questions.
2026-10-23 20:16:15 -04:00
Pranit 915d439bd2 Show /permissions in a footer panel
Running /permissions with no arguments printed the mode, configured
rules, session grants, and saved rules to the transcript. It now opens a
read-only panel in the footer, like /status, and prints nothing. Each
configured rule, session grant, and saved rule is a bullet, and saved
rules show the ID that /permissions revoke takes. The panel updates when
an approval adds a grant. Subcommands such as /permissions auto and
/permissions remember still confirm in the transcript.

The /status panel becomes a shared info panel that both commands use.
Tests that ran /permissions only to put text in the transcript now run
/allowlist.
2026-10-23 19:29:02 -04:00
Pranit 13ff1ab627 Keep the MCP catalog generation 32-bit for wasm32
wasm32 has no 64-bit atomics, so the SDK's WASM builds failed to compile
the catalog's generation counter. Bindings only compare it for equality,
so 32 bits is enough.
2026-10-23 19:28:46 -04:00
Pranit b31b528760 Check the transcript's last row under a short /status panel
With FX_RECORD set, fx prints a recording notice under the banner, so a
12-row terminal has no room left for the banner above the panel. The
tests now check that the last row of the transcript stays in view.
2026-10-23 19:24:29 -04:00
Pranit 333d476456 Restart only the MCP servers a config change affects
An /mcp add, remove, approve, or reject used to build a new MCP host and
restart every server. The runtime now applies the change in place through
Engine.reload: servers with the same config keep their process, connection,
sign-in, and calls; removed or changed servers stop and their calls end;
a changed or re-added server starts once its own old process has exited.

The engine copies each server's environment and compares it by content, so
reading the same config again no longer looks like a change.
2026-10-23 19:10:17 -04:00
Pranit 20331680eb Keep the transcript in view under the /status panel
In a short terminal the panel could take every row but three and cover
the turn in progress. Closing it redrew the rows it had covered, which a
terminal could already have scrolled into history, so they showed twice.
Output written while the panel was open could lose them instead.

The panel now gets the rows an inline picker gets, which leave the end of
the transcript in view, and scrolls the rest with the arrow keys. The E2E
reader scrolls a panel that does not fit to read every line.
2026-10-23 18:54:07 -04:00
Pranit 8a964aca1d Show the session ID in a /status panel
- /status opens a panel in the footer instead of writing a notice to the transcript. Esc or Enter closes it and leaves nothing behind.
- The panel's first line shows the open session's ID and whether it is saved yet, so it can be resumed with fx --resume <id>. A shell that can't save sessions shows none.
- The open panel updates when a running turn saves the session or finishes.
- When the panel doesn't fit, the arrow keys scroll it and the hint says so. Long values such as the workspace path wrap instead of being cut off.
- When profile settings can't be read, the panel shows a Settings line and leaves out auth, instead of printing an error with no status.
- A fork made before the first prompt now counts as saved: /status shows it as saved, and the exit line prints its resume command.
2026-10-23 15:59:13 -04:00
Pranit 314c01479c Merge pull request #1435 from vercel-labs/esteban/reanchor-variation-selector-glyphs
Keep cells aligned after glyphs with a variation selector
2026-10-23 14:02:33 -04:00
Pranit 3f312edcd6 Merge pull request #1417 from vercel-labs/esteban/mcp-status-line-startup
Show the MCP attention count once servers start and clear it after a prompt
2026-10-23 14:02:03 -04:00
Pranit 2f8503970d Merge pull request #1416 from vercel-labs/esteban/mcp-menu-attention-group
Group the /mcp list and tidy each server screen
2026-10-23 14:01:21 -04:00
Pranit 6ba6b47621 Merge pull request #1437 from vercel-labs/fazxes/mcp-oversized-replies
Explain oversized MCP replies and stop fetching them again
2026-10-23 13:55:26 -04:00
Pranit 6d6a4e271f Merge pull request #1436 from vercel-labs/fazxes/mcp-logout-everywhere
Keep MCP logouts in effect across terminals
2026-10-23 13:55:19 -04:00
Pranit 7a64a790c2 Merge pull request #1421 from vercel-labs/fazxes/withhold-earlier-images
Keep earlier images from blocking new messages
2026-10-23 13:44:51 -04:00
Pranit e38fa77e8d Describe the oversized call flag for HTTP answers too
The flag on a call's end covers an HTTP answer over the message limit as
well as a stdio line, so its comments now say so.
2026-10-23 12:20:44 -04:00
Pranit 80b2bbecfb Explain oversized MCP replies and stop fetching them again
- When a stdio MCP server replies with more than 16 MiB, the model is told the reply was too large and fx restarted the server, instead of "the server went away".
- When an HTTP MCP server replies with more than 16 MiB, fx ends the call at once and tells the model the reply was too large, instead of "the server went away".
- fx no longer resumes an HTTP stream (2025 protocol) to fetch an oversized reply again. Before, it downloaded the same reply up to two more times.
- fx no longer requests an oversized tool list again from an HTTP server on the 2026 protocol.
- The debug trace logs each dropped reply with the server, transport, and reason.
2026-10-23 12:10:32 -04:00
Pranit edb1505f8b Keep MCP logouts in effect across terminals
- /mcp logout in a shell now also revokes the login another terminal saved after the shell started. Before, that login stayed valid at the server.
- After another terminal logs out of an MCP server, a shell that still holds the login revokes it the next time it would renew it, instead of renewing it and saving it back.
2026-10-23 12:06:10 -04:00
Pranit ae687a4879 Merge pull request #1425 from vercel-labs/fazxes/encode-inline-fuzz-seeds
Encode inline fuzz seeds the way the tests read them
2026-10-23 11:56:30 -04:00
Pranit 3009e80783 Merge pull request #1424 from vercel-labs/fazxes/skip-substitutions-in-command-labels
Read command substitutions in assignment values as one word
2026-10-23 11:56:16 -04:00
Pranit f8a7917ef1 Merge pull request #1430 from vercel-labs/fazxes/stop-processes-before-force-kill
Stop a command's processes before force-killing them
2026-10-23 11:56:04 -04:00
Pranit 683f84a0df Merge pull request #1428 from vercel-labs/fazxes/skip-next-version-check-in-sdk-tests
Keep the Next package test off the npm registry
2026-10-23 11:55:53 -04:00
Pranit be3805bc79 Merge pull request #1422 from vercel-labs/fazxes/show-mremap-to-thread-sanitizer
Let ThreadSanitizer see pages that mremap releases
2026-10-23 11:55:42 -04:00
Pranit 2fe8583744 Merge pull request #1419 from vercel-labs/fazxes/review-git-remote-helpers
Review git fetch from repositories that name a remote helper
2026-10-23 11:55:31 -04:00
Pranit 131b41e1bc Merge pull request #1418 from vercel-labs/fazxes/allow-slow-conversion-notice
Let resumed v1 session tests accept the slow conversion notice
2026-10-23 11:55:20 -04:00
Pranit a9ffecc223 Merge remote-tracking branch 'origin/main' into fazxes/withhold-earlier-images 2026-10-23 11:35:25 -04:00
Pranit 83f5093375 Merge pull request #1431 from vercel-labs/fazxes/terminal-cancel-and-cursor-report
Fix a lost split character and a cursor report crash
2026-10-23 11:34:37 -04:00
Pranit 59f8def998 Stop parents before their children before a kill
A shell with job control runs the rest of a list when its foreground job
stops, as it does when the job dies. Stopping children first gave such a
shell the same gap the kill pass had, so the stop pass now goes parents
first: the root, then each tracked process in the order the tracker found
it. The kill pass keeps its order, because by then nothing is running.
2026-10-23 00:24:29 -04:00
Pranit 00a8ac243b Stop a command's processes before force-killing them
When fx force-kills a command after a cancel, a timeout, or a lost owner, it
sends SIGKILL to each process it tracks, children before parents. A parent
shell whose child died first could run its next command before its own
SIGKILL arrived, so cancelling `sleep 30; rm -rf out` during the sleep could
start `rm -rf out`. The tracker now sends SIGSTOP to every process it would
kill before the SIGKILL pass, so none of them can act on another's death.
Cleanup after a command finishes on its own gets the same order.
2026-10-22 23:23:52 -04:00
Pranit d824f609cf Merge pull request #1427 from vercel-labs/fazxes/flat-shell-schema
Advertise shell, subagent, and read_tool_result with top-level arguments
2026-10-22 23:11:38 -04:00
Pranit 5fc25a1100 Merge pull request #1426 from vercel-labs/fazxes/shell-reload-approval
Ask again when a shell call reloads startup files
2026-10-22 23:11:18 -04:00
Pranit 1aee3e4c8e Fix a lost split character and a cursor report crash
- Write a character that a terminal read splits right before a CAN or SUB byte as a replacement character, as the same bytes in one read already do. It was dropped, or joined with continuation bytes after the CAN into a different character.
- Stop fx from crashing on a cursor position report in origin mode when the cursor sits above the top margin, for example after DECRC or VPA. The report now gives row 1.
2026-10-22 23:10:05 -04:00
Pranit 2445b4d496 Assert subagent corrections without printing
The helper in the subagent decoder tests printed the correction it could not find, and the direct-write audit rejects a print outside a test block. It now fails with a plain assertion.
2026-10-22 20:04:37 -04:00
Pranit 524f975731 Test the risk note for substitutions in safe assignment values
The command approval prompt now shows the forced-removal note and the
safer-tool hint for a command such as CI=$(echo a b) rm -rf dir, because the
assignment no longer ends inside the substitution. Assignments the analysis
doesn't peel, such as X=$(...), still get no note.
2026-10-22 19:32:05 -04:00
Pranit 5d65a09700 Keep the Next package test off the npm registry
Before next dev --webpack compiles anything, it waits for Next's version
check, a fetch to registry.npmjs.org with no timeout of its own. While the
registry was slow, dev printed that it was ready and then answered nothing,
so test-next-package.mjs gave up after 30 seconds. Each Next server the test
starts now preloads sdk/tests/no-npm-registry.mjs, which fails that request at
once, so Next takes its "staleness unknown" path and the test needs no npm
access.
2026-10-22 19:00:06 -04:00
Pranit a7ad972621 Encode inline fuzz seeds the way the tests read them
A fuzz test reads its input through std.testing.Smith, which reads a slice
as a little-endian u32 length and then that many bytes. The inline seeds of
the older fuzz tests were plain strings, so each lost its first four bytes
to the length: "one" replayed as an empty input, and "split every field"
read "spli" as a length. The fx tests now wrap their seeds in
fuzz_seed.slices, which writes that length first. The session manager
imports nothing from fx, so its two targets keep their seeds as saved
inputs in tests/fuzz/corpus.
2026-10-22 18:44:46 -04:00
Pranit e1442f49fa Run shell calls that leave out an unambiguous action
A shell call without `action` now runs when its fields fit exactly one action: `command` with run fields means run, `session_id` with `chars` or `yield_time_ms` means interact, and `session_id` with `force` means stop. fx adds the action before the call is stored or checked against permissions, so the approval covers the command that runs. A bare `session_id`, or a `command` next to a `session_id`, is still rejected with a correction.
2026-10-22 18:43:49 -04:00
Pranit df0878ebe7 Advertise subagent and read_tool_result with top-level arguments
Both tools list their fields at the top level instead of under a `request` object with one variant per action or selector, so a model no longer writes that nested JSON by hand. Each field's description says which action or selector it belongs to. A rejected subagent call now names the field it got wrong and the fields each action takes, instead of returning only an error code. Calls that still wrap their arguments in `request` keep working, and saved wrapped calls replay with top-level arguments.
2026-10-22 18:43:49 -04:00
Pranit e52b144f22 Advertise the shell tool with top-level arguments
The shell tool schema lists `action`, `command`, and the other fields at the top level instead of nesting them under `request` with one variant per action. With the wrapper, models that wanted several commands at once sometimes sent two `request` keys in one call, which fx rejected as invalid JSON arguments. Each field's description now names the actions it applies to, a field used with the wrong action is still rejected with a correction, and corrections suggest a top-level retry. A call that wraps its arguments in `request` still runs, and saved shell calls are replayed as they were sent.

Tool schemas also keep the description of an object-valued property, which the `shell` field of the shell tool and the `request` field of `read_tool_result` were missing.
2026-10-22 18:43:49 -04:00
Pranit 7c82eedc95 Ask again when a shell call reloads startup files
A remembered command approval is not meant to cover a shell call that reloads the user's startup files, because the reload changes the environment the command runs in. The check read `reload` only from inside a `request` wrapper, but shell calls are admitted with top-level arguments, so `reload: true` never matched and the remembered approval still applied. The check now reads top-level arguments as well as an exact wrapper.
2026-10-22 18:43:33 -04:00
Pranit 5a5c14cb2d Merge pull request #1415 from vercel-labs/esteban/auth-callback-page-style
Match the browser sign-in page to the fx.sh design
2026-10-22 18:22:47 -04:00
Pranit efec77550a Read command substitutions in assignment values as one word
word_end, which finds where a leading NAME=value assignment ends before the
command classifier looks for the command, now reads $(...), ${...},
backquotes, and $'...' the way the shell does, so a space inside a
substitution no longer ends the assignment early. The command_classification
fuzz target now generates substitutions and leaves them out of its lexer
comparison, because the lexer reads $ and backquotes literally.
2026-10-22 18:21:26 -04:00
Pranit 0afa721701 Merge pull request #1413 from vercel-labs/fazxes/mcp-sign-in-screen
Show MCP sign-in progress and keep one sign-in per server
2026-10-22 17:15:32 -04:00
Pranit 4d612d58f2 Merge pull request #1412 from vercel-labs/fazxes/mcp-login-store-v3
Move MCP logins to a store older builds can't erase
2026-10-22 17:14:55 -04:00
Pranit cb203d19b0 Let ThreadSanitizer see pages that mremap releases
ThreadSanitizer does not intercept mremap, so when std.heap.PageAllocator shrinks or moves a block on Linux, the released pages keep their access history and the next thread to use those addresses appears to race with the one that released them. That produced the race reports in the MCP HTTP large-answer test. Sanitized x86_64 and aarch64 Linux builds now link an mremap that makes the same change with munmap, and with mmap, a copy, and munmap when a block moves.
2026-10-22 17:04:04 -04:00
Pranit 6dd69eded2 Keep earlier images from blocking new messages
When fx could not confirm that a model accepts images, because the model catalog was unreachable or did not list the model, every later message in a session that had attached an image failed with "Unable to verify image support for this model". Only an image attached to the current message can fail it now. Earlier images go inline to a model the catalog lists with vision, and through the vision tool's image references to a model without vision. Otherwise they stay in the session and leave the request as a note for the model, and an unreachable catalog tells the user once per message.

- The image route core separates images attached this turn from earlier ones, which follow the tool image rule.
- `bun run verify` in `tests/tla/image_route` gains a stage that drives the built binary through `fx ask` sessions and has TLC check every turn against the model.
- The `image_route` and `earlier_image_notes` fuzz targets check the core's laws on generated event sequences and the notes left for earlier images.
2026-10-22 16:33:06 -04:00
Pranit 43b3080078 Review git fetch from repositories that name a remote helper
git runs a git-remote-<helper> program, which it may find on PATH, for a repository written <helper>::<address> and for a URL whose scheme has no built-in transport. Auto mode now sends those git fetch commands to review, including after --. https, ssh, git, and file URLs still run without review.
2026-10-22 16:30:59 -04:00
Pranit b51e635db7 Let resumed v1 session tests accept the slow conversion notice
fx ask writes one notice when converting a session an older fx saved takes over 150 ms. The tests that resume a copied v1 session through fx ask now strip that one leading notice before requiring empty stderr, so a slow runner no longer fails them.
2026-10-22 16:20:50 -04:00
Pranit ca146491e9 Merge pull request #1393 from vercel-labs/fazxes/add-nightly-fuzzing
Add a fuzz build step, nightly fuzzing, and shell parity tests
2026-10-22 14:00:59 -04:00
Pranit 4625e465c6 Stop the build with a message when fuzz setup runs out of memory
The fuzz corpus setup in `build.zig` reports a failed allocation with `std.process.fatal`, as it does every other failure, instead of panicking.
2026-10-22 13:01:27 -04:00
Pranit 16116537a1 Read the shell parity setting from the test environment
Tests never set up the environment `io.getenv` reads, so the shell parity tests never saw `FX_SHELL_PARITY_ALL_SHELLS` and skipped a missing shell even when it was set. They now read it from `std.testing.environ`.
2026-10-22 13:01:27 -04:00
Pranit 551e7d26d5 Report shell parity failures through test assertions
A parity failure now shows what fx parsed against what the shell ran, and which command or shell failed, through `std.testing.expectEqualStrings` instead of printing it, and keeps its error name.
2026-10-22 13:01:26 -04:00
Pranit d072810a68 Use camelCase names for the OAuth helpers
Eleven functions in oauth.zig, chatgpt_oauth.zig, and grok_oauth.zig
used snake_case names, against the repository's camelCase rule for
functions. They and their callers are renamed, and nothing else
changes.
2026-10-22 12:44:20 -04:00
Pranit fd726cc0d3 Reject wrong-type grep_files and glob_files arguments
Both tools replaced a wrong-type value with its default and ran anyway.
A string "true" for case_insensitive searched case-sensitively, a
numeric path searched the whole workspace, and an unknown mode fell
back to the default, while the model believed it got what it asked for.

They now answer with a message that names the field, as the other tools
do. A missing field or null still means the field was not given.
2026-10-22 12:44:20 -04:00
Pranit aad62a74c2 Check the ChatGPT account ID before saving a login
Grok refused to save an account ID that it would reject when loading,
but ChatGPT saved it anyway. A sign-in or refresh with such an ID looked
fine, and the next start then reported that saved credential storage
was unavailable.

Both providers now check before saving, so that sign-in or refresh
fails right away. The setting that kept the two apart is gone.
2026-10-22 12:44:19 -04:00
Pranit a4b04c7f97 Share one session store between ChatGPT and Grok
chatgpt_session.zig and grok_session.zig were the same store with a
different file, lock, log label, and pair of error names, and the two
copies had started to drift. Both now come from subscription_session.zig
with those values. Each still returns its own error names, and only the
errors it returned before.

The one real difference stays, as an explicit setting: Grok refuses to
save an account ID that it would reject when loading, and ChatGPT
doesn't. ChatGPT's issuer and auth file name exports had no users and
are gone.
2026-10-22 12:44:19 -04:00
Pranit ad0f58c3b7 Remove the unused failing allocator from the terminal store tests
The only test that used it was removed when fx started loading shell
startup files once per process, and the allocator stayed behind with no
callers.
2026-10-22 12:44:19 -04:00
Pranit 89746a2874 Classify provider HTTP failures in one place
Six transports turned an HTTP error status into a failure kind with the
same switch, and four wrapped their stream callbacks around the event
sink with the same adapter struct. A new status had to be added in six
places, and a missed copy would misclassify retries for one provider.

FailureKind.fromHttpStatus and the EventSink callback adapters now live
in stream_provider.zig, next to the types they produce, and every
transport uses them. Each status maps to the same kind as before and
each adapter emits the same event. New tests pin both.
2026-10-22 12:44:19 -04:00
Pranit a73774ef12 Merge pull request #1397 from vercel-labs/fazxes/mcp-send-failure-test
Test that a failed MCP send frees its queued copy once
2026-10-22 12:33:07 -04:00
Pranit d0a52d0056 Merge pull request #1403 from vercel-labs/fazxes/approval-answers-during-redraws
Keep approval answers when unrelated redraws are pending
2026-10-22 12:32:44 -04:00
Pranit 64e7c07b0d Merge pull request #1411 from vercel-labs/fazxes/resume-search-load-more
Offer Load more in the resume picker only when more sessions match
2026-10-22 11:55:42 -04:00
Pranit 6d8193f2c6 Require every shell for the parity tests in the Linux unit job
The Linux unit test job installs zsh, so it now sets `FX_SHELL_PARITY_ALL_SHELLS` and the shell parity tests fail there instead of skipping when sh, dash, bash, or zsh is missing.
2026-10-22 11:54:35 -04:00
Pranit 2e9a945fb9 Note which sanitizer test failures are timing 2026-10-22 11:52:55 -04:00
Pranit 3bdf08a8fa Keep frame pointers and symbols in sanitized builds 2026-10-22 11:52:55 -04:00
Pranit 2f2a0e7679 Run the ThreadSanitizer tests in ReleaseSafe with step time limits 2026-10-22 11:52:55 -04:00
Pranit 37e79dcdd5 Add a nightly ThreadSanitizer run of the unit tests 2026-10-22 11:52:55 -04:00
Pranit fe32145a55 Fuzz the MCP wire decoder 2026-10-22 11:52:55 -04:00
Pranit 31f9af3cd0 Fuzz ACP frame writing and line reading 2026-10-22 11:52:55 -04:00
Pranit 1b3173a451 Fuzz the web fetch URL policy and response parser 2026-10-22 11:52:55 -04:00