Repository navigation
v1.0.620
-
fix(gh-1743): a structured compaction that hits its total-budget
watchdog (the factory's 300stotalBudget) no longer aborts the
live session.AutoCompactorFactory.run()now applies the
mechanical local-trim fallback (keepRecentTokens, pairing-repaired,
[context trimmed locally]marker — the same valve the classic
pass ladder ends on) instead of failing fast; the honest
both-roles-failed verdict is kept only when nothing is droppable. -
fix(gh-1636): auto-compaction stops dumping the internal checkpoint
summary into the console — the default report is ONE marker line
(● auto-compacted · engine: N → M tokens (F freed · P%)); the records
line + fenced summary moved behind the newtui.showCompactionDetails
opt-in (default false), and the manual/compactstill prints the full
block. The headless log face no longer streams the summarizer deltas by
default (the gh-1433 E4 live stream is part of the same opt-in; the
gh-1430… reasoningliveness line still covers the window). Nothing
is lost — the summary stays in the session file, reachable via
compact_expand. -
fix(test): gh-1732 — the
wrap_ansi_line_perf_testsustained-contention
validation leg spikes 3 of the 5 doubled runs per attempt instead of 4
(CI run 39546301132, core shard 2/4: the old shape left exactly ONE
real-wall-clock-timed doubled sample per attempt, and real jitter on
that single sample — the very shape being modeled — inflated all three
attempts' min ratios to a 3.30x best against the 3.2 bound while the
real 500k growth legs on the same runner stayed green). 3-of-5 is the
documented live-red shape ("≥3 of the 5 timed runs"): median-of-5
[c,c,s] is still a spiked value, so the leg still proves the
pre-gh-1532 median harness fails, and the min harness now rides two
clean doubled samples per attempt. -
fix(#1705): CLI fresh-install boarding asks the provider choice FIRST —
the same list the/add-providerpicker shows (presets,Customlast) —
and onlyCustomcontinues into the api-type wizard; a preset opens its
own flow with the api type prefilled. Previously boarding opened the
custom wizard directly, cold-asking "api type" before any provider was
chosen. -
feat(providers): gh-1704 — first-class Codex/ChatGPT credential export,
CI refresh action, and Provider Queue integration (Copilot-export
parity).fa provider export <name> [--check]resolves the saved
ChatGPT login's OAuth blob from its secure-store slot, refreshes it when
stale, and prints the shell lines (OPENAI_API_KEY+OPENAI_BASE_URL
= the Codex backend — these tokens never serve api.openai.com); the
refresh token never prints and--checkreports credential health with
the SSO caveat./provider chatgpt import [path]adopts an existing
codex loginsession (~/.codex/auth.json) into the same slot — no
browser round-trip..github/actions/chatgpt-codex-authmints a fresh
access token from aCHATGPT_REFRESH_TOKENsecret in CI (direct curl,
no third-party action); invalid_grant fails fast and loudly.
providersQueue:entries speakchatgpt-codex: an omittedapiKeyEnv
resolves the Keychain slot automatically (in-memory refresh), a new
refreshTokenEnv:field mints the initial access token on slot
activation, and a raw Bearer token inapiKeyEnvis tolerated by
chatgpt_codex.dart(no more FormatException — proactive/reactive
refresh degrade, a 401 surfaces as the auth death the queue fails over
on). -
feat(bench): gh-1681 — leaderboard submission readiness. ATIF
trajectories: newexportAtifJsonwhole-snapshot exporter
(lib/src/trajectory/atif_export.dart, ATIF-v1.7 — the schema harbor
0.23.0's flagship adapters emit) behind a headlessfa trajectory export [--format atif] [--output path]verb; the harbor adapter runs
it as a bounded best-effort exec after the agent phase on every
terminal path, so<trial>/agent/trajectory.jsonreaches the hub with
the other agent logs (the leaderboard static analysis requires one per
rewarded trial) andresult.jsonmetadata carries an
atif_trajectorypresent/missing marker. The golden fixture
(bench/harbor_fa/fixtures/atif_sample.json) is pinned Dart-side and
cross-validated against harbor's pydanticTrajectory+
validate_trajectoryintest_atif_contract.py. Workflow: a
submission: truedispatch input turns the submission rules into hard
setup preflights (full task coverage, ≥5 attempts/task, no
agent-timeout override, pinned dataset) and makes the new per-shard
ATIF presence gate fail hard; default runs gain only a warn-only scan
(artifact flow unchanged). Runbook: docs/leaderboard-submission.md
(pinned dataset + @latest content-hash recording, quota-window runner
policy, lb CLI steps). -
fix(headless): gh-1589 — the headless session becomes a SEALED artifact:
no session-JSONL writes land after the run's agent phase ends. New
Session.seal()(state in the writer, no file-format change): raised in
runHeadless' finally and the REPL teardown as the LAST session action
(close order: drain → usage fold → seal → exit, one implementation both
paths — REG-2), it makes every writer (_persistMessages,
shell_job_registrysnapshots, compaction, anyappendCustomEntry,
leaf moves) drop late appends with exactly ONE stderr warning per
writer class — never a throw into an unhandled zone; the exit code
stays the turn's terminal outcome (issue #413 semantics untouched). The
settle→reaction path is sealed with the file (I2): the drain wrap-up
raises a fence (_reactionRunsSealed) so a background-shell settle or
task completion arriving after the final answer's drain — including the
registry's one-turn-deferred listener racing_finishHeadlessDrain's
check-to-return window (the 65gGW7b 10:13:42–10:14:00 burst) — does
board bookkeeping only: no_startRun, no model request, no record.
An honest drain's records (final answer → seal) stay legal (E7); the
seal is per-run state, so resume/replay/session_repairare unchanged.
Adapter side (bench/harbor_fa, I3/AC4+AC7):run()/perform_task
returns only after the fa process exited on EVERY path — the ladder
kill became terminate-and-reap (SIGTERM → bounded exec await → SIGKILL
escalation → pgrep confirm), and the external-cancel paths (harbor's
own wait_for firing first, the no-knobs exec, external-cap-only mode)
now fire the kill + bounded reap before propagating, with a loud
agent-phase exit guarantee degradedstderr note when fa survives both
signals (never a silent proceed, never an unbounded hang). -
fix(bench): gh-1643 — a trial killed mid-stream no longer reports 0/None
usage. The fold read only FINALIZED assistant messages, so a run killed
before its first assistant message (the costliest rows) folded to silent
zeros. Both folds now price a danglingmodel_request_summaryat its
KNOWN request-side volume (system prompt + outbound message chars at the
shared chars/4 rate, estimated input; output unknown) and mark the usage
incomplete instead:UsageChainScan.usageIncomplete→usage.json
(usageIncomplete: true, written only when set so completed folds stay
byte-identical),SessionUsage.usage_incomplete→ the tb adapter's
usage-guard.jsonrow and harbor'sagent_result.metadata, and the
post-mortem stampsusage_incompleteon every results.json row it
patches from a partial fold. Harbor also exports the run outcome class
(agent_result.metadata.failure_mode: agent_timeout self-kill /
external_kill / completed / crashed / never_started) so downstream
classification stops relying on exception.txt archaeology; a summary
superseded by another summary is flushed instead of silently dropped on
both sides. Never-started trials keep the gh-1638/gh-1576 audit-only
precedence (no fake usage). -
fix(stuck-calls): gh-1632 — a stuck tool call in an interactive session now
escalates like everywhere else.effectiveStuckTool()defaulted every
non-headless host toStuckFollowUpMode.advisorywhen no explicit
agent.stuckTool:was configured, so the fa 1.0.580 incident session (a
deadgit fetchin the REPL) fired itstool_stuckflag at the 300 s
threshold and then hung for 27 more minutes — advisory mode never cancels,
retries, or converts; only the owner's manual steering broke the deadlock.
The autonomous default (cancel → retry once → background-convert, then a
session-visible escalation) now applies in EVERY mode;advisoryremains
an explicit opt-out (agent.stuckTool.followUp: advisory), never an
implicit default. -
fix(bindings): gh-1624 — the gh-1570 binding preflight no longer kills
translating-proxy factories._validateModelEndpoint's vendor-prefix
verdict was fatal on ANY endpoint, soFA_PROVIDER_TYPE=anthropic+
bifrost's/anthropicpath +zai/glm-5.3exit-64'd every boot on
1.0.576 (the prefix is a routing key on a translating proxy, not a
dialect declaration). The vendor-prefix check now fires ONLY on the
endpoints that carry a dialect verdict — the three family-exclusive
canonical vendor endpoints (api.anthropic.com, api.openai.com,
generativelanguage), mirroring the family verdict's scope: those keep
the gh-1570 fail-fast, ROUTERS (OpenRouter/Copilot/DIAL/kimi — the
prefix is a routing key there too; OpenRouter's canonical ids are
anthropic/claude-*) and unknown/private endpoints carry the mismatch
as aProviderModelBinding.warningsentry, never exit 64. The warning
is rendered on EVERY resolve path (gh-1624 review): theFA_PROVIDER_*
boot notice, the provider queue (per entry at boot, on queue reload,
and in/provider queue test), and the--model/--base-urlflag
revalidation — a rider never boots silently.
Breaking-change note for 1.0.576 upgraders behind proxy
factories: the migration is thewire:FA_PROVIDER_CONFIG key —
documented now (README env-preconfig section,
docs/agents/providers.md §Translating proxies,--help) with its
caveat: the override engages only when the declared wire DIFFERS from
the provider record's own kind (record anthropic + wire: anthropic
still runs the checks). REG matrix (AC1/AC2/AC3) pinned in
test/model_roles/provider_model_binding_test.dart; AC4 IT drives a
bifrost-shaped loopback proxy through the production anthropic adapter
(test/model_roles/bifrost_proxy_binding_test.dart). -
gh-1596: the first-run wizard (
site/setup.sh) no longer crashes or
corrupts input undercurl | shon dash (Ubuntu sh) or busybox ash
(Alpine):scripts/gen_installers.dartnow renders the provider/mode/
approval menus from the live provider catalog into plain POSIX sh — the
bashisms that killed dash (printf -v,read -p) and silently corrupted
busybox ash are gone; the hidden API-key prompt usesstty -echo+ a
by-nameread(dash has noread -s) and the key never rides printf
argv; EOF/Ctrl-C mid-wizard aborts non-zero with no half-written
~/.fah, and non-TTY runs print guidance and exit 1. The wizard's temp
config ismktemp-created (no predictable, symlink-followable fixed
path). A new path-triggeredinstaller-portabilityCI leg proves the
script under dash + busybox ash (ubuntu) and bash (macOS) on every
site/**or generator change. -
fix(cli): gh-1572 —
faboot no longer rewrites~/.fah/config.yaml
from a lossy config.persistConfig(bin/fah_runapp.dart) built a
freshCliConfigcarrying only some fields andpersistConfig()ran
unconditionally at boot, so every launch silently dropped the user's
spills:,subagents:,jobs:,headless:,agent:,quota:,
tui:,trajectory:,waiting:,power.holdandauto_update
settings (first reported on 1.0.547). Three cooperating fixes: the
boot save is nowsaved.copyWith(<live overlays>)so every parsed
section rides along by construction;CliConfig.copyWithreplaces the
drifted hand-rolledwithCustomProvidersfield list (itself dropping
spills/subagents/waiting/jobs/agentMode/misuseBreaker/streamThinking
insidesaveCliConfig's merge step); the_diskPreservedSections
disk backstop covers every known top-level key.spills:renders in
toYaml(newSpillsConfig.toYaml) andagent.misuseBreaker: false
persists too; an unchanged save no longer rewrites the file at all.
Rework round: the boot snapshot re-syncs from disk after every
sanctioned mid-session settings write (onUserConfigFileChanged— a
cleared context cap, anauto_updatekey drop or a global compaction
pick is no longer resurrected/overwritten by the next save in the
same session);validateAgentSection(the strictfa config check/
config set/ settings-hub mirror) accepts the emitter's
agent.misuseBreakerandagent.stuckToolkeys sofa config check
never false-fails on fa's own output; clearing the context cap drops
only the cap key instead of the wholeagent:block (the sibling
mode:/misuseBreaker:keys survive); and theprovidersQueue:
block (no typed renderer) joins the disk backstop so a save can never
silently delete the user's queue. Rework round 2 (merge + guards):
validateAgentSection's per-key switch became the_agentKeyValidators
table (the two gh-1572 keys pushed the switch form to complexity 13 —
over the repo's CRAP ratchet; the table keeps the mirror complete at
complexity 4), and the last two direct USER-config writers —
_writeTtsrSectionand_writeMcpSection— fire the re-sync hook, so
a mid-session ttsr rule toggle or mcp server edit survives the next
persist too (the writer roster insettings_flow_config_sync.dartis
now exhaustive). -
gh-1606: the
copilotprovider accepts fine-grained PATs
(github_pat_…) directly — PAT mode skips thecopilot_internal/v2/token
exchange entirely (GitHub 404s every PAT exchange) and sends the raw PAT
as the Bearer against the configured Copilot base URL with the official
Copilot CLI's first-party identity headers
(copilot-integration-id: copilot-developer-cli,
user-agent: copilot/<fa-version> (<os>) term/unknown, installed by the
host at startup). OAuth device-flow tokens keep the exchange +
proactive-refresh flow unchanged; a PAT 401/403 surfaces the error
instead of a doomed refresh retry. HeadlessCOPILOT_GITHUB_TOKENnow
works with a PAT. Connect flow: the paste hint explains PAT mode, the
dead-PAT case surfaces at the model fetch, and/provider exportcopies
the saved Copilot account's full token to the clipboard (account picker
on several entries; the token never prints to the transcript). -
gh-1575: macOS clipboard paste fixed for TIFF-only pasteboards + the
Ctrl+V text fallback._readMacosprobes«class TIFF»after PNGf/JPEG
miss and converts viasips -s format png(a Preview/browser copy used
to dead-end onclipboard holds no image; a failed conversion keeps the
unavailable fall-through and every scratch file is cleaned up); new
readPasteboardText(pbpaste/ xclip+wl-paste / PowerShell
Get-Clipboard) behind the same injectableProcessRunner/platform:
seam, and Ctrl+V with a TEXT clipboard now inserts the clipboard text
into the composer instead of printing the image probe's dead-end reason
— bracketed-paste routing, gesture pairing and silent-failure semantics
unchanged. Review pass: the WindowsGet-Clipboardread pins UTF-8
console output and normalizes embedded\r\n(OEM-code-page mojibake),
the Linux text backends pintext/plain;charset=utf-8(an image-only
clipboard can no longer leak bytes as "text"), the library doc no longer
overclaims theFA_FAKE_PASTEBOARDseam, andFaTuiCallbacksmoved to
afa_tui_callbacks.dartpart file keepingfa_tui.dartunder the
2800-line gate. -
gh-1555 (W1, lazy/windowed-everywhere reads): the legacy full-open call
sites migrate to windowed opens, so a session ledger past the gh-1073
2 GiB full-open backstop stays openable on every user-facing read path.
The TUI session picker,/resume, and the boot name-resolution scan
open windowed and resolve the display name through paged reads; the
subagent transcript preview opens the child windowed (tail only); the
_loadSessionmarathon fallback no longer degenerates to a refused
full open when the boundary walk exhausts its page cap (it proceeds
with the tail-anchored windowed session; the boot cap owns over-window
residue); and the headlessfa trajectoryresolver opens windowed,
reading the whole ledger for the cost/inspect views through the new
SessionTree.ensureWholeLedgerResident— a sequential forward scan in
bounded strips (WindowedSessionStorage.loadAllOlder), not the
boundary-walk's doubling strips (whose per-strip record cap measured
~13x read/parse amplification over the whole file). The same change
block-prepends paged chunks in_indexChunk(oneinsertAllper
list), removing an O(n²) the suspended-eviction walk paid per record.
Trajectory export streaming (AC3a), split-and-retain rotation
activation (W2), and the opt-in write-side caps (W3) remain follow-up
work. -
gh-1605: the FinalizeGate gains a "grader parity" named check. A
produced entry point (script/CLI the verifier will run itself) is only
verified when it survives the grader's own invocation: the checklist
must re-run each produced entry point from a different working
directory in a fresh subprocess with a clean env, capture stdout/
stderr/exit code, and diff against the earlier in-session run — any
divergence (different output, relative-path artifacts, locale/
encoding-dependent output) is afailledger item, never a pass.
Closes the modernize-scientific-stack__nwbgy7a near-miss (every
in-session checklist item passed, the verifier's clean-cwd subprocess
collapsed Station 102 to Station 101's mean, reward 0.0). REG coverage:
the parity procedure catches cwd- and env-dependent entry points and
does not false-positive cwd-independent ones
(test/agent/finalize_gate_grader_parity_test.dart), and an IT replays
the evidence shape end-to-end — divergence detected and persisted as a
failledger item before finalize. -
gh-1603: unattended runs can no longer end as a clean success on empty
model responses. Loop: from the second empty-hiccup retry on, the request
carries a continuation nudge user message ("your last reply was
empty/truncated — continue the task"; exact-text constant
emptyContinuationNudge, emitted with MessageStart/End so the transcript
stays truthful) — a cap-truncated reasoning model re-streamed verbatim
repeats the empty outcome, the nudge breaks the symmetry; the classifier
treats that exact message as transparent, so the turn's partial-content
window (gh-1577) survives a nudged retry. Headless: the empty-reply nudge
budget rises toAgentCliConfig.headlessEmptyReplyNudges(default 2 — the
session ends only after 3 consecutive unrecoverable empties; interactive
keeps the #1085 M2b one-nudge semantics), and a run that still settles
empty prints the explicit__FA_INCOMPLETEmarker and exits 1 — a
distinguishable failure for the bench adapter, never a silent exit 0. -
gh-1577: an empty assistant completion after partial content (assistant
text, tool activity, or a truncation signal earlier in the turn) is a
bounded-retry provider hiccup — up toemptyHiccupRetries(default 3)
re-streams with exponential backoff (emptyHiccupBaseDelay,
emptyHiccupMaxBackoff, both threaded through theAgentfacade,
AgentWiringSpec, and the CLI config), and exhaustion ends the run with
a loudStopReason.error(headless exit 1, interactive error + prompt)
instead of the silent exit-0 settle that killed the gh-1577 trials.
Clean empties keep the #1085 M2b one-nudge semantics. The abort always
wins: a cancel racing the final retry settlesStopReason.abortedfor
any empty shape (the exit-contract race is pinned by tests), and the
backoff sleep is cancellation-aware. -
gh-1574: SQLite reads work on installed binaries — bundle libs land where
the binary looks. The AOT binary's native-assets resolver reads
<exe_dir>/../lib/<name>(thedart build clibundle layout), but every
POSIX installer copied the bundle libs beside the binary
(<install_dir>/lib), soread path=x.dbdied with a raw FFI dump
(Couldn't resolve native function 'sqlite3_initialize' … Failed to load dynamic library '~/.local/lib/libsqlite3.dylib'); it only worked on machines
with a manual symlink in~/.local/lib. Fixed the layout in all three
install paths —site/install.sh(incl. the quarantine-xattr pass and the
source-build advice),install_local.sh, andfa update
(bin/self_manage.dartcopyBundleLibs, now public and layout-pinned by
tests; also aligns Windowsfa updatewithinstall.ps1, which was
already correct) — plus clean degradation in the FFI engine:
Sqlite3Engine.openReadOnlyclassifies the dart:ffi resolver's
library-load failure (isSqliteDynamicLibraryLoadFailure) and throws a
cleanSQLite is unavailable — reinstall or run fa updatenote instead of
the raw resolver dump; a Dart-level system-library fallback is impossible
with package:sqlite3 native assets (build-time asset mapping, no loader
override). Installs already broken by the old layout self-heal: on the
bundled library's first failed load the binary moves the stale
<exe_dir>/libsqlite3.*into<exe_dir>/../lib/and retries the load
(repairStaleSqliteBundleLayout) — so onefa updatesuffices (the
successor binary repairs on its first SQLite read; a pre-fix binary cannot
repair itself), and the stale<bin>/libleftovers disappear as they
migrate. The POSIX installers also report a failed lib copy instead of
swallowing it (site/install.shwarns naming the lib dir; same in
install_local.sh), andsite/install.sh.templatecarries the same
layout so regenerating the installers cannot resurrect the old one. -
gh-1556: bench pipeline stops bleeding score + fa deadline awareness.
Bench: the harbor ladder kill uses the[f]abracket trick and is
wrapped so a kill failure can never replace theTimeoutError(the
pre-fix self-matchingpkillclassified TB 2.0 ladder kills as agent
crashes — a SOLVED task scored 0); progress-watch (stall-gap 360s per
the gh-1430 watchdog invariant, flat abs ceiling 3600s) is the
bench-harbor.yml default on every shard, with the harbor adapter
picking the gap-awareProgressWatchfor parity with the
terminal-bench adapter; the aggregate counts completeness by
result.jsonpresence (errored trials report as their own breakdown
line; exit 1 only on genuinely lost trials) and renders the full
report — table, errored breakdown, ledger row — to
$GITHUB_STEP_SUMMARY(TB1 parity). Both bench adapters export
FA_DEADLINE_EPOCH_MS(effective cap − 120s wrap-up margin) into the
agent session and audit the exact value. fa core: deadline awareness —
FA_DEADLINE_EPOCH_MS/--timer/--deadline//timerwrite one
session clock, the per-turn context carries atime-left:line while
armed, and one-shot system notices steer wrap-up at ~50%, T-3min
(drain background jobs), and T-30s; absent deadline = byte-for-byte
today's behavior. -
gh-1522: tag-sourced versions — the git tag is the single source of
truth. The release-bot file bumps are retired: cutting a release = a
tag + GitHub Release (zero commits to main, zero file mutations).
Both pubspecs carry the fixed0.0.0-devplaceholder (CI guards
hand-bumps), the publish job stamps the staged pubspec + generated
changelog section from the tag (scripts/stamp_staged_release.sh,
staged version == tag guarded pre-upload), store builds stamp
--build-name/--build-numberfrom the derived tag version
(pubspec.lockstops churning across releases), and
install_local.shstampsversion.txtfromgit describe. -
feat(usage): gh-1460 — the
fa-tokens:segment-close line carries the
session's model id, so every token row the factory publishes is
attributable to a model and therefore priceable. The fold now captures
each segment's LAST-SEEN model id (the model that served the final
request, the same string settings/UI display — mid-session fallback
take-overs attribute the segment to the model that actually finished it;
zero-filled fallbacks never clobber a seen model) and the pinned line
shape grows an OPTIONAL"model":"<id>"key after"segment"— omitted
when no model was observed, so legacy lines parse unchanged and the
pinned regex (usageTokensLogPattern, gh-1241 AC7) keeps matching both
shapes. usage.json's schema is untouched (the model is rebuildable from
the chain, I6); old consumers using tolerant JSON ignore the new key,
and the repo's single strict consumer (the pinned constant) is updated
in the same change. -
fix(test): gh-1460 rework — the
wrap_ansi_line_perf_testgrowth-shape
legs measure the doubling ratio with the MINIMUM over 9 runs instead of
the median of 5 (CI run 38112524699, core shard 0/4: a scheduling pause
inflated the doubled-size median — 12.09ms against a true ~7ms — and
tripped the 3.2 bound at 3.65x on a genuinely linear wrap). Runner
jitter only ever inflates a wall-clock sample, so the min converges on
the true compute time; genuine superlinearity still trips the ratio
(the gh-1496 quadratic candidates measure ~4x by min). The absolute-
budget legs keep the median — their gh-1357 headroom already prices in
jitter. A synthetic regression test pins the pause-proof property.