160 Commits
Author SHA1 Message Date
Pranit 99b8ad47d7 Refresh PGSO corpus expectations 2026-10-26 18:17:33 -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 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 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 7ae4d4107b Add a fuzz build step, nightly fuzzing, and shell parity tests
- `zig build fuzz` builds only the fuzz tests listed in `tests/fuzz/targets.json`. `-Dfuzz-target=<name>` picks one target, and `--fuzz=N` searches for new failing inputs.
- `zig build test` replays each target's saved inputs from `tests/fuzz/corpus/<target>/` on every pull request.
- A nightly Fuzz workflow fuzzes every target for about 20 minutes, uploads any failing input, and opens or updates an issue for that target. A pull request that changes the fuzzing setup fuzzes each target for two minutes.
- The shell parity tests run every command form fx runs without review through sh, dash, bash, and zsh, with and without `extendedglob`, and check that each one runs exactly as fx parsed it. The `shell_parity` target checks generated commands the same way.
2026-10-22 11:52:23 -04:00
Pranit 7e2f67d607 Port the remaining session tests to the v2 store
The E2E files, TLA+ drivers and the long-turn memory benchmark that read
v1's session files now read the v2 log. Fixes for what those tests found:

- Resuming a subagent child directly fails with v1's message.
- fx session recover copies a v1 conversation whose permissions.json is
  malformed or empty, without its rules, and says so.
- fx doctor counts the v1 sessions not converted yet and names each one it
  cannot convert.
- Salvage leaves out a side file that is missing or whose size or digest
  changed.
- A v1 log whose checkpoint splits a tool call from its result still
  converts.
- --resume last and fx session last fail with NoReadableSessions when they
  skipped an unreadable v1 session.
- A converted session keeps a last turn that never ended, marked
  interrupted.
- fx session --id on a damaged v1 session names the file and the reason.
- Prompt images are also kept as blobs, so the oversized-image notice names
  a file that still exists after a resume.
- A session reopened after fx was killed mid-retry shows the finished tool
  calls and results, not the partial reply.
- An update relaunch from a session with nothing saved opens a fresh
  session.
- Converting a schema-2 session captures images it saved only by path, as
  v1's resume did.
- Recalling a prompt from history uses its saved image bytes, so a damaged
  captured file no longer refuses the recall.
- fx -c says the remembered session could not be read when v1's bookmark is
  unreadable.
- The /resume picker orders v2 and unconverted v1 sessions newest first,
  like fx sessions.
- Wiring.tla models one store, without the backend switch or the usage
  ledger.
2026-10-19 10:25:59 -04:00
Pranit 5fb0158484 Lower the PGSO size ceiling to 7.0 MiB
The macOS arm64 candidate is 6.57 MiB after the size reductions in #1164.
Release eligibility also requires 0.25 MiB of headroom, so a 7.0 MiB
ceiling leaves about 190 KB for growth before a release needs trimming,
instead of the 1 MiB that 7.8 MiB allowed.
2026-10-14 13:09:27 -04:00
Pranit e37b74f117 Match the PGSO helper pass to Zig 0.17 and LLVM 22
Zig 0.17 names generic instantiations __func_N instead of __anon_N and
renames allocBytesWithAlignment to allocBytesAligned, so the helper
patterns follow. The IR size tests default to the pinned LLVM 22 tools and
the production target triple, and the benchmark profile-use test expects
the profile preparation passes.
2026-10-14 10:53:05 -04:00
Pranit ccccbd18ff Accept wrapped Darwin bitcode from the PGSO helper pass
LLVM's bitcode writer wraps Darwin modules in the 0x0B17C0DE header, so the
outline-helpers output failed the raw-header check that guards Zig's
emitted bitcode. The check now accepts both valid magics, and the IR size
tests validate the real pass output with it.
2026-10-14 10:53:05 -04:00
Pranit c963d70d11 Shrink the macOS arm64 release binary
The PGSO pipeline runs two LLVM passes through the pinned LLVM C library.
The first keeps small std helpers like Allocator.free, Writer.writeAll, and
byte list cleanup out of line before instrumentation, so Zig stops copying
them into thousands of call sites. The second replaces copies of mostly
undefined error-return constants with stores of their defined bytes. The
candidate link also passes -no_compact_unwind, because newer Apple linkers
otherwise add about 160 KB of unwind data to a binary built without unwind
tables.

Trace logging formats byte, integer, enum, and bool placeholders through one
shared writer with byte-identical output. The turn loop's three per-request
history walks are noinline, so their hot loops keep their own profile and
the rest of the loop can compile for size.
2026-10-14 10:53:05 -04:00
Pranit 7c9086e8fd Refresh the PGSO corpus test after the usage rename 2026-10-13 22:47:24 -04:00
Pranit a41fc2d421 Read /usage from provider usage reports
/usage and fx usage now show what the provider reports instead of
counting locally, and fx no longer keeps its own usage ledger.

- AI Gateway: with fx login, your spend on the team and your user
  budget; with an API key, that key's spend. Today, the last 7 days and
  the calendar month (UTC), with per-model tables and a details screen
  for each model.
- Codex: the ChatGPT plan's limits, token activity, and usage by model
  and surface, read from the Codex backend.
- Grok: the plan's allowance, usage by product, bought credits, and
  pay-as-you-go spending, read from the Grok CLI proxy.
- /usage draws df-style tables, neutral in every theme and sized to
  their content at any width. Opening it shows cached numbers at once
  and refreshes in the background, then every 15 minutes while open and
  on r, at most once a minute. Finished days are cached under
  ~/.fx/usage-cache and read at most once per UTC day.
- fx usage takes --period today, 7d or month, prints the same tables,
  and --json includes every provider's view. The /cost alias, 24h and
  30d are gone.
- src/usage/ holds all usage logic; the TUI and CLI only call its API.
- The session and profile usage ledgers, the usage sidecar file,
  Gateway generation lookups, and provider billing outcomes are removed.
  Saved sessions with old usage data still load and resume; old usage
  fields and files are left in place and ignored.
- A completion's tool calls still never run after another process takes
  over the session: each host confirms it still owns the session writer,
  without writing, before tools run.
- ACP reports context use from each completion's token counts and no
  longer sends a cost.
- An existing ~/.fx is made private when a session reopens, and a usage
  fetch creates a missing ~/.fx with owner-only access.
- E2E tests drive /usage and fx usage against loopback fakes for every
  provider.
2026-10-13 04:02:31 -04:00
Pranit bc51246646 Merge origin/main into fazxes/native-workspace-indexer 2026-10-12 17:51:07 -04:00
Pranit e94fb092d4 Keep the ui-activity benchmark's output in PGSO round logs
The heavy ui-activity measurement runs the benchmark pair under
hyperfine, which discards the benchmarked program's stdout and stderr.
When the benchmark failed its own timing gate it exited 1 after printing
its ratios and `error: UiActivityProgressRegression`, but the job showed
only hyperfine's "non-zero exit code 1", so a timing breach could not be
told apart from a crash or a changed fixture.

Hyperfine now passes that output through with `--output=inherit`, the
form it accepts alongside `--style none`, and run_checked keeps it in each
round's log, which the job already uploads. A passing round adds one short
line per benchmark.
2026-10-12 15:44:45 -04:00
Pranit 0a76c1ee38 Merge origin/main and port the indexer to Zig 0.17
main moved to Zig 0.17. The branch keeps its deletion of
file_index_cache.zig, ignored_dirs.zig and workspace_files.zig, whose
only changes on main were 0.17 renames, and keeps its own side of
grep_search.zig and glob_files.zig. AGENTS.md takes main's refresh plus
the indexer and safe git notes.

The branch's code moves to the 0.17 names: std.Io.Dir.path,
Allocator.print, std.mem.print, builtin.target and @backingInt. The
wildmatch test builds its repeated strings with a local helper because
the indexer cannot import text_utils, and two folder selection test
calls gain the try that 0.17 requires.
2026-10-12 02:20:21 -04:00
Pranit 31f018c407 Count both new E2E files in the PGSO corpus test
- This branch and main each added one verification E2E file and raised the expected candidate count from 59 to 60. With both merged there are 61.
2026-10-11 06:10:28 -04:00
Pranit 28fd1a00ed Merge origin/main into fazxes/native-workspace-indexer 2026-10-11 05:59:04 -04:00
Pranit d6b9be0885 Merge remote-tracking branch 'origin/main' into fazxes/zig-0.17-upgrade
# Conflicts:
#	src/builtins/mcp.zig
#	src/core/mcp/mcp_runtime.zig
#	src/core/mcp/project_config.zig
#	src/core/mcp/tool_search.zig
2026-10-10 18:38:05 -04:00
Pranit c459fcb468 Classify the MCP verbs E2E as verification-only for PGSO
MCP-v2 is opt-in behind FX_MCP_ENGINE=v2, so its commands should be verified by the PGSO candidate without shaping its hot and cold layout. The PGSO self-test lists now include the file and the new candidate count.
2026-10-10 15:45:36 -04:00
Pranit 3cf0c1037b Add MCP-v2 as an opt-in MCP engine
- FX_MCP_ENGINE=v2 runs MCP servers through MCP-v2, a new MCP client. Without it, fx keeps using the current engine.
- MCP-v2 speaks MCP 2026-07-28 and 2025-03-26 through 2025-11-25 over stdio and Streamable HTTP.
- MCP-v2 starts a server the first time something needs it, such as a matching tool search, instead of at launch.
- fx mcp list, show, add, remove, login, logout, approve, and reject manage servers. list and show accept --json.
- /mcp opens a menu of servers with their status, tools, and actions, and /mcp <command> runs the same commands in the shell.
- At startup, fx names project servers that are waiting for approval.
- fx mcp login signs in through the browser, or with --no-browser by pasting the address the browser ends on.
- fx mcp logout revokes the token when the server supports revocation.
- MCP-v2 keeps its logins apart from the current engine's, in the macOS Keychain when it's available and otherwise under ~/.fx/mcp-credentials/.
- Quitting fx sends each running stdio server SIGTERM and exits without waiting for it.
- For 2025 servers, a call's idle timeout pauses while the user answers the server's question. The overall limit still applies.
- Server instructions reach the model with that server's tools.
- A server that doesn't register fx as a client shows a hint to set oauth.client_id in mcp.json.
- Over ACP with MCP-v2, fx advertises stdio and HTTP servers only, and refuses "type": "acp" servers with a reason.
- Over ACP with MCP-v2, editor-supplied servers marked always-loaded keep their tools in every turn.
- Over ACP with MCP-v2, editors receive elicitation/complete for URL questions, including a completion that arrives before the user accepts the URL.
- Editors that advertise session.notices receive fx's notices as ACP Session Notices instead of assistant text, with either engine.
2026-10-10 01:04:41 -04:00
Pranit 23930e634a Test that a poisoned repository runs none of its programs
- Add an end-to-end test with a repository whose own config names fsmonitor, hooks, filters, diff drivers and a pager, and check that interactive startup, @-completion, glob_files, grep_files, auto-run git status and fx pr run none of them while still answering from the repository.
- Classify it as verification-only for macOS arm64 PGSO and give it a CI shard weight.
2026-10-08 16:32:02 -04:00
Pranit becf3256df Search workspace files without git
- Run glob_files and grep_files on the native indexer's listing, so a search starts no git process and sees files created a moment earlier, including untracked files in subfolders.
- Reuse the saved @-completion listing for a search of the workspace or one of its ordinary folders while it is current, and scan the search root otherwise.
- Search file contents on several threads, with the same results and the same cut at the match cap as a single-threaded search.
- Keep searching hidden folders inside a repository, and skip them elsewhere unless the search path or pattern names one.
- Reuse a listing cut at the entry cap while nothing it read has changed.
- Check in CI that @-completion, glob_files and grep_files never start a process.
2026-10-08 16:31:53 -04:00
Pranit 8925f00b74 Add the native workspace indexer foundation
- Add src/core/indexer/, which reads gitignore rules, repository layout, and git config as files and never starts a process.
- Match git's ignore decisions for nested .gitignore files, info/exclude, and the global excludes file, including include and includeIf config.
- Report the filter drivers a repository's own config defines, and fail closed when that config cannot be read.
- Check in CI that outside code imports only indexer.zig and that the indexer imports nothing that can start a process.
2026-10-08 13:15:40 -04:00
Pranit 20ec33679d Fold unrolled loops before PGSO profile instrumentation
Unrolling constant-trip loops ahead of the default pipeline removed the
per-element counts, but the raw unrolled code also made the optimized
benchmarks slower. The ui-activity candidate ran about 19% slower than
its plain O2 control, and an unprofiled build with the same preparation
was 11% slower on its own. Running instcombine before and after the
unroll and simplifycfg after it brings that build within about 1% of
plain O2, while eqlBytes keeps its Zig 0.16 counter layout.
2026-10-06 02:27:06 -04:00
Pranit f9d2a818e1 Unroll constant-trip loops before PGSO instrumentation
Zig 0.17 emits array bitcasts, such as the [4]u8 reads in std.mem.eql,
as per-element loops that the optimizer later folds into single loads.
Instrumentation runs before loop unrolling, so the training profile
counted every element. On the e2e-cli and e2e-tui-resize shard,
mem.eqlBytes rose from 1.5 billion counts to 22 billion while every
other function stayed level. That one function then held most of the
profile, so the cold cutoff marked nearly all other code minsize and a
supplemented function landed exactly on the cutoff.

Run sroa and loop-unroll-full ahead of the default pipeline when
generating and using profiles. Both sides see the same IR at the
instrumentation point, so CFG hashes still match, and eqlBytes gets the
same counter layout it had on Zig 0.16.
2026-10-06 01:26:57 -04:00
Pranit c43f117763 Link the PGSO candidate against a stub the Xcode 16.4 linker reads
Zig 0.17 bundles the macOS 27 libSystem stub, which lists arm64e.x1
targets. The Xcode 16.4 linker on the macos-15 runners rejects that
architecture, so the temporal candidate link failed. Link against a copy
of the pinned stub with only those target entries removed. The arm64
link never resolves against them, and the evidence records the hashes
of both stubs.
2026-10-06 00:27:24 -04:00
Pranit ab462c9ed3 Profile the seed bitcode in distributed PGSO candidates
Zig 0.17 can number anonymous symbols differently on another runner,
so the same commit emits bitcode that differs in a few symbol names.
The candidate job rebuilt the bitcode and required it to match the
seed byte for byte, which failed on every run.

Copy the seed's hash-verified bitcode instead, the same way the job
already reuses the seed's control binary and merged profile.
2026-10-05 23:31:27 -04:00
Pranit f16f201834 Pass the Zig global cache through the environment
Zig 0.17's `zig build` no longer accepts --global-cache-dir, so the
binary size builds and the PGSO pipeline set ZIG_GLOBAL_CACHE_DIR for
the same directories.

The binary size check now builds the base commit with the Zig version
that commit pins in build.zig.zon. A pull request that changes the
toolchain could not build its base with the new compiler, and the
comparison now reports the size change across the toolchain bump.
2026-10-05 20:10:43 -04:00
Pranit 4186e98154 Allow the compactor to import the testing allocator
The compactor's allocation-failure test now backs its sweep with
testing_allocator.no_resize. That file imports only std, so it belongs
with the shared basics the boundary check already allows.
2026-10-05 19:54:45 -04:00
Pranit b1d515851a Upgrade to Zig 0.17
Build fx with Zig 0.17.0. build.zig.zon requires it, every workflow
installs it, and PGSO qualification uses LLVM 22.1.8 to match.

Zig 0.17 removes `**`, `errdefer |err|`, `@cImport`, and the old type
info field lists. The source moves to `@splat`, a comptime
`text_utils.repeat` helper, split cleanup functions, and the new
field_names and field_types arrays. The Node-API addon translates
node_api.h with a build step. Test fakes checked with `@hasDecl` become
pub, because `@hasDecl` no longer sees private declarations.

`zig build test` gives the test binary its fx and source paths through
a generated test_paths module, since the build no longer exposes install
paths for environment variables.

Zig 0.17's SafeAllocator grows a small allocation in place only when it
sits at the end of a bucket, and buckets are never reused, so an
allocation-failure sweep could count a different number of allocations
on each run. Sweeps and allocation-count tests now use
testing_allocator.no_resize, which never resizes in place.

allocRemaining now treats its limit as inclusive. readFileToEnd keeps
rejecting a file of exactly max_bytes by reading through
appendRemaining. Direct allocRemaining callers for remote responses now
accept a body exactly at their limit.

Zig 0.17's memset stores a full vector per iteration, so the Linux
memset replacement is removed. macOS release binaries now require macOS
15, Zig 0.17's default minimum.
2026-10-05 19:44:53 -04:00
Pranit 3deb16329c Merge remote-tracking branch 'origin/main' into fazxes/linux-first-ci
# Conflicts:
#	CONTRIBUTING.md
2026-10-05 08:39:14 -04:00
Pranit a13c6e5732 Load shell startup files once per fx process
- Commands that use the user profile run the login shell's startup files
  (zsh or bash) once per fx process. fx captures the resulting PATH,
  environment, aliases, functions, and shell options, then restores them
  for every later command. Subagents share the same capture.
- fx captures again on the next command after a watched startup file
  changes, such as .zshrc, .zprofile, .bash_profile, or .bashrc.
- /shell reload makes the next command run the startup files again. The
  model can do the same with reload: true on shell.run.
- A reload clears the session's remembered command approvals, and cleared
  approvals no longer show in the approvals list or the status count.
- When a capture fails, exceeds 8 MiB, or takes longer than 10 seconds, fx
  runs the startup files for every command and says so once in the
  interactive shell, in fx ask, and in ACP.
- When restoring a capture fails inside a command, fx reruns that command
  with full startup.
- In auto mode, a routine command that uses a captured alias or function
  goes to the safety review, and routine commands go to the review while
  captures are failing.
- The shell tool description tells the model that startup files apply,
  that cd, export, and alias changes do not carry over between calls, and
  how zsh treats unquoted globs and words that start with =.
- The terminal host daemon and the tmux backend are removed, along with
  the FX_TERMINAL_HOST_* environment variables. tty terminals run inside
  the fx process, end when fx exits, and show as ended on resume.
2026-10-02 19:08:41 -04:00
Pranit 5126a07b3f Merge remote-tracking branch 'origin/main' into fazxes/linux-first-ci 2026-10-02 02:36:10 -04:00
Pranit f80232a0aa Detect E2E helpers that read the platform through node:os
The shard guard treats platform() from node:os as a platform branch, but
the detector only matched process.platform, so a helper written that way
could skip macOS. Match both, list the helper rule in the detector's
header, and say "reads the platform" in the docs.
2026-10-01 17:35:27 -04:00
Pranit 043436148f Fix the macOS dispatch fetch depth and detect platform E2E helpers
The dispatch fetch depth used `event == dispatch && 0 || 2`. Expressions
treat 0 as falsy, so every event got depth 2, origin/main was never
fetched, and a manual dispatch failed at detect. Put the dispatch case
last so it evaluates to 0, and capture the merge base before calling
the detector so a missing base fails with git's own error.

The detector ignored shared E2E helpers, so a change to a platform
branch in tests/e2e/tmux-helpers.ts could skip macOS. Treat a helper
that reads process.platform on either side of the change as a macOS
input.
2026-10-01 16:39:29 -04:00
Pranit 539ca4650b Merge main and refine Ultrafast indicators 2026-10-01 14:28:00 -04:00
Pranit ebba84f05e Close macOS detection gaps and stop pull requests saving Zig caches
The macOS detector now fails the check when it cannot read either
revision or the platform test list, instead of reporting that macOS is
not needed. It also treats Linux and BSD code paths as platform code,
because macOS takes the other side of a Linux branch. Windows-only
branches still skip macOS. Replaying the last 150 commits on main moves
macOS runs from 72 to 76.

The macOS platform E2E files now run in two weighted shards that share
one baseline-CPU build. tui-performance and tui-resume join the list
because they branch on the platform.

Linux Zig jobs restore a cache keyed by the Zig version and build files,
and only runs on main save it. Pull request runs no longer add cache
entries that push main's out of the quota. The macOS jobs never run on
main, so their Zig cache is off.

Build & Test now also requires the SDK jobs, which Publish libfx relies
on. Manual dispatches get their own concurrency group so they cannot
cancel the push run on main. The N-API build pins Node 24, Binary Size
smoke-tests every target, and the shellcheck job syntax-checks each
script instead of only the first.
2026-10-01 14:02:22 -04:00
Pranit 88cd75ba64 Merge remote-tracking branch 'origin/main' into fazxes/linux-first-ci
# Conflicts:
#	.github/workflows/ci.yml
#	.github/workflows/full-ci.yml
#	AGENTS.md
#	CONTRIBUTING.md
2026-10-01 05:17:16 -04:00
Pranit 8a6563cda3 Verify Ultrafast before the first embedded terminal request 2026-10-01 05:16:26 -04:00
Pranit c6b629ead8 Gate CI on Linux and run macOS only when a change needs it
- Run the full deterministic E2E suite on Linux in four shards on every pull request, plus the MCP conformance baseline, all against one shared build of fx.
- Run macOS arm64 unit tests and platform E2E files only when a change touches macOS-specific code, build.zig, macOS signing, or the native SDK addon, or carries the ci:macos label. Otherwise the macOS arm64 check passes without a macOS runner.
- Remove the four-platform Full CI workflow and the separate cross-target compile jobs.
- Smoke-test the Linux arm64, macOS x86_64, and macOS arm64 binaries in Binary Size, and reuse the base build for later pushes to the same pull request.
- Build the SDK WASM surfaces and Node-API addons once and share them across the SDK test jobs.
- Run PGSO on pull requests only when the PGSO pipeline changes, and keep release PGSO evidence for 90 days.
- Run the long-turn memory, libfx runtime, and live ACP jobs only on request.
- Limit libfx publishing to an archive check, an install smoke test, and a registry byte comparison.
- Add a warning when an E2E file passes only on retry.
- Remove the unused Prepare Release workflow and document the new CI rules.
2026-10-01 05:12:19 -04:00
Pranit c59e6a35ae Merge simple-compaction into compactor-shared-history 2026-10-01 03:58:37 -04:00
Pranit 110823f201 Add opt-in Ultrafast mode 2026-10-01 03:54:09 -04:00
Pranit ff7ed7e849 Merge main into simple-compaction 2026-10-01 02:54:29 -04:00
Pranit a4144f3701 Make the compactor independent of the session module
The compactor imported three helpers from src/core/session/session.zig.
Two of them, rawHistoryTurnCount and contextHistoryRange, only count and
slice saved turns over shared types, so they move unchanged into
src/core/shared/history_range.zig and every caller imports them from
there. The third, appendHistoryChatMessages, is the session's own
projection of saved turns into chat messages, so it stays in the session
and callers hand it to the compactor through compactor.Request, like the
model caller and the record store.

The compactor now imports only shared basics and model configuration, and
the boundary check no longer allows session.zig. Behavior is unchanged.
2026-09-30 22:14:11 -04:00
Pranit 73559f73db Add an experimental v2 session store to fx ask
- Add --sessions-v2 to fx ask (before or after ask) and FX_SESSIONS_V2=1. With either, fx ask saves the session as an append-only log in ~/.fx/sessions/v2/<id>/.
- Resume those sessions with --resume last and --resume-id; they are only visible with the flag on. --no-save still writes nothing.
- Save each finished exchange while the turn runs. After a crash, the prompt and finished tool calls are kept, and the next resume marks that turn interrupted.
- Store tool output side files in ~/.fx/session-files/<id>/, owner-only like the log.
- On resume, cut a half-written last line. A damaged line elsewhere stops the resume with InvalidSessionFormat and leaves the file unchanged. A session another process holds fails with SessionBusy.
- Report a missing session with the current store's errors, NoSavedSessions and SessionNotFound.
- Recover profile usage from v2 sessions after a crash, as for current sessions.
- Leave subagents off in fx ask while the flag is on.
- Keep the current store as the default; it now ignores the v2 folder.
2026-09-30 17:47:36 -04:00
Pranit a8c1350b03 Bound compaction summary requests and keep saved output reachable
Summarize a compacted part too large for one request in several
requests, oldest first, each folding into the next. A single turn that
does not fit keeps the start and end of its long texts in the request;
its saved records keep them whole.

Name the saved whole output of a clipped tool result in its record and
in the summary request, so it stays reachable after compaction.

Keep the configured provider routing when the summary falls back to
another model, and tell the summarizer to treat tool output and quoted
text as information.

Restore image_baseline in the compaction decision trace, record a
compaction that finds nothing to compact, and drop duplicate helpers,
an unused result field, test-only window options and the second record
store interface.
2026-09-27 02:52:26 -04:00
Pranit 4123ae2006 Replace context compaction with one compactor
- Manual /compact, automatic compaction, and context overflow recovery all run through src/core/compactor.
- User messages stay exact, the newest compacted turns keep their final replies word for word, and the rest of the assistant's work is summarized.
- Every compacted turn and tool call is saved unchanged as M1, M2, ... and T1, T2, ..., and read_tool_result opens and searches them.
- The summary uses the conversation's model at its lowest reasoning level, with one retry on another model family through the gateway.
- Automatic compaction starts at auto_compact_percent of usable input (10 to 80, default 80); FX_AUTO_COMPACT_PERCENT overrides it.
- Checkpoints written by the previous compactor still load.
- CI checks that code outside the compactor uses only its front door.
2026-09-26 21:45:26 -04:00
Pranit c87d1247f1 Fix PGSO corpus scenario count 2026-09-21 18:20:23 -04:00
Pranit 571b65b98a Merge pull request #918 from samcx/codex/slack-cli-install
Install the Slack workspace bot through the CLI
2026-09-21 12:36:46 -04:00
Pranit f3bf3cf47c Revert "Keep per-step runtime context at the message tail for prompt cache stability" 2026-09-20 03:33:10 -04:00