Repository navigation
v1.0.643
-
feat(gh-1844): media slots never fall back to the main connection
implicitly — SDK/CLI, app store, and the JS bridge resolve a slot
without an override as UNCONFIGURED with an actionable
"configure the slot" error; the main connection serves a slot only via
the explicit per-slotuseMainConnection: trueopt-in (YAML
models.slots.<slot>.useMainConnection/ app's "Same as main
connection" pick, preserved throughmedia_models.json). Image and
video generation accept multimodal input (text + reference images:
sandbox paths, http(s) URLs, base64) on capability-declaring
OpenRouter endpoints (input_references+ declared/probed
input_modalitiesgate; unsupported endpoints fail before any network
call); the OpenRouter video job API is a first-class video dialect
(submit → poll → mp4). Newdecidetool: structured decisions via
OpenRouter's Decisions API (/v1/systemone) — bool/choice/score
questions with calibrated probabilities, structured error results, no
streaming. -
feat(gh-1834): the FinalizeGate contract counts a parity/conformance
claim as verified only with negative + combination self-checks — new
numbered step 2.6 inprompts/cli/finalize_gate.md(refused
incompatible/mutually exclusive options, empty/corrupt inputs, boundary
values, must-NOT-work cases, alongside happy paths; a blanket
"everything matches" is not verified). Universality: abstract example
only, no benchmark/task/flag names in the prompt; the step is
conditional, so tasks with no conformance claim see no gate change.
Ledger/verdict wire formats unchanged; validated end-to-end by the
scripted IT-4 replay (real CLI + real bash) whose persisted
task_ledgercarries the rejection rows. -
fix(gh-1832): release v1.0.635 published without
fa-extension.zip— the
landing-page download 404'd. The extension's only producer (the daily
build-macos.ymlbuild-extension leg) derives its tag as "latest+1 at its
own dispatch moment"; on 2026-10-24 thev1.0.635tag was cut by
auto_releasebefore those legs ran, so the extension (and the DMGs)
landed on an orphanedv1.0.636draft while the tag train published
v1.0.635 — every leg green, nothing asserted the published asset set.
Nowscripts/release_required_assets.txtis the single source of truth,
scripts/assert_release_assets.shgates therelease-provenancepublish
flip (a missing asset leaves the release a draft and fails the run
loudly) and a newrelease-asset-smokeci.yml job post-publish probes
every landing-page release link + the full required set (red on non-200).
Remediation: the exact-tagfa-extension.zip(built from the v1.0.635
tree by run 40064876732) was uploaded to release v1.0.635 — the landing
link serves 200 again. -
fix(gh-1820): the arm64 DMG leg's stale size floor is reseeded onto the
shipped v1.0.634 shape — run 4004758753 (post-#1815 SPM-lane adoption)
passed the min-platform gate and the full build, then failed the
size_manifest.py+5% budget onApp.framework/App+5.1%
(25,020,112 → 26,284,144, the accumulated Dart AOT of #1793/#1794/
#1796/#1805/#1806); the committedscripts/size_budget/fa-macos-arm64.tsv
was still the pre-#1096-diet floor (TOTAL 210.5 MB vs the artifact's
116.4 MB), so the documented intentional-growth remediation applies:
the floor is now the exact--reseedoutput for the failing run's
artifact manifest (26 rows ≥ 64 KiB + TOTAL 116,386,369, from the run
log's printed table — the zip never uploaded past the gate), locking in
Fa 23.4→18.6 MB, quickjs_bridge 950→844 KB; new
test/ci_1820_macos_arm64_size_floor_test.dartgates the shape green
through the REALsize_manifest.py checkand pins the committed floor
byte-identical to a--reseedround-trip. -
fix(gh-1814): the desktop DMG lane ADOPTS the SwiftPM lane — Flutter
3.47.7 removed the FLUTTER_SWIFT_PACKAGE_MANAGER disable knob
(flutter/flutter#193978), so the gh-180 pure-CocoaPods pin became a no-op
and the min-platform gate (which forbade any SwiftPM state) failed both
DMG legs on every pinned-3.47.7 run (39991663029, 40022900373); the
#1813 escape (pin 3.47.4) is unreachable on the ~60kB/s runner link.
scripts/assert_macos_min_platform.shnow ASSERTS the SPM lane instead:
SwiftPM state expected afterflutter build macos --config-only,
generated package floor ≥ the app floor (the silent bump must not skip —
the original gh-180 killer), no SwiftPM product above the app minimum,
vendored inappwebview manifest floored at 14.0 if it ever reappears.
Floors were audited and need no raising: Runner pbxproj 3×14.0, Podfile
:osx, '14.0', generated floor 14.0 on 3.47.7, vendored podspec 14.0
(its Package.swift stays deleted — the plugin rides the CocoaPods half of
the hybrid lane via Podfile.lock). The dead env pin left the build-macos
job env; the SDK pin reverts #1813 back to 3.47.7. The ci.yml macOS debug
leg gets the same treatment (gh-1814 review): its
FLUTTER_SWIFT_PACKAGE_MANAGER=0 pin — a silent no-op on the floating
3.47.x stable it resolves past the knob's removal — is removed and the
lane comment corrected; the leg rides SPM enabled as it already has been
(run 399572012 green). -
fix(gh-1805): modifier chords no longer type their base letter into the
composer while the slash menu is open — the vendored dart_tui decoder
puts the base letter into the keystroke'stextfor control bytes
(Ctrl+S →ctrl+swith texts), and the slash-menu edit cluster was
the one insert surface missing the_isCommandKeystrokeguard, so a
chord pressed mid-menu poisoned the typed prefix (/memorylanded as
/smemory). Plain letters and Shift+letters still type and filter. -
feat(1515): ONE model-limits settings surface — the settings hub's
standalone "Model parameters" row is retired and bare/model-edit
(TUI) aliases into/settings→ Model capabilities, pre-focused on the
active model. The caps flow gains the model-edit preset ladders +
Custom… free input for context window / max output (a preset pick
writesmodels.overridesAND rebuilds the active model on the spot —
the old flow's live-mutation semantics, now durable), every field row
shows the current EFFECTIVE value with its source layer (pinned/
role override/catalog), the thinking row is an omp-style selector
(only levels the model supports, per-rung budget metadata, current
level preselected), and the hub row describes the effective summary
(200K ctx · 16K out · thinking off) instead of a pin count. The
scripted/model-edit <field> <n>form and the line-mode limits print
are unchanged. -
feat(gh-1746): opt-in session disk retention —
sessions.retentionin
~/.fah/config.yamlcaps the sessions root and sweeps whole session
units (transcript + sidecars) oldest-last-used-first at boot, exempting
live, named, young, and the current session; nothing is EVER
auto-deleted without the section (byte-identical legacy boot). Adds
fa session clean [--dry-run], the/sessions+fa session list
usage footer under a cap, and header-cwd/path-escape guards so a
planted session header can never steer a delete outside any root. -
fix(gh-1763): a
bashtool command running broad process kills can no
longer signal its own shell or the harness. Scripts are delivered from a
per-exec private temp file (sh -c '. "$1"' sh <script>) so the wrapper
argv never carries the script body (pkill -f <pattern>cannot
self-match its ownsh -c;$0/$#/exit codes/set -e/stdin stay
identical), a PATH shim forpkill/killallplus akill()prelude
guard exempt the harness pid chain + process group (exported as
FA_PROTECTED_PIDS) and the invoking shell from the child's own kills
while legitimate victims (stuck test runners, background servers) are
still terminated, and headlessSIGTERMaborts now log
fa: SIGTERM received …so a child-issued kill never masquerades as a
user Ctrl+C (exit code contract unchanged: 130 graceful, 143 escalate). -
fix(gh-1778): the release gate no longer reds a fully-green shard when
GitHub's cache service degrades. v1.0.621'sCore tests (shard 2/4)was
killed by its owntimeout-minutes: 12with 2535/2761 tests green — both
cache steps had stalled ~133 s on keys that existed (degraded service,
not eviction), coldpub get+ native-asset hook builds delayed the
first test to ~5 min, and the shard needed ~14 min against a budget
sized for the warm ~8.8 min wall. The job budget is now 20 min (sibling
test-leg band), and bothtest-corecache steps are best-effort
(timeout-minutes: 2+continue-on-error: true) — a failed restore is
slower setup, never a red shard.test/ci_core_shard_timeout_guard_test.dart
pins the budget and the best-effort cache contract (RED-first on the old
YAML). -
feat(#1761): the 32KB project-context budget never cuts silently again.
A file truncated to its head excerpt now carries an extended
<!-- From: path — N bytes, only the first M shown -->marker plus an
explicit MUST-read-the-full-file instruction (offset/limit/ranges,
prompt copy inprompts/context/project_context_read_full.md); a file
dropped entirely leaves a one-line tombstone naming its path instead of
vanishing; and the user gets a dim boot line per truncated/dropped file
(CLI line mode prints directly, TUI re-prints after the banner, the app
logs through its AppLog boot-notice seam). NewprojectContext:config
section:maxBytes(default 32768) —0disables the section entirely
and reads no files (fast boot); unknown keys are tolerated with a note.
Leaf-first allocation is unchanged. -
fix(gh-1757): the Android build no longer re-enters the sdkmanager
download roulette for CMake. v1.0.619'sbuild-androidleg died at
:jni:configureCMakeDebug[arm64-v8a]— the:jnimodule (transitive
jnipackage via path_provider_android) wires externalNativeBuild.cmake
with no version, so AGP auto-installed its defaultcmake;3.22.1
mid-configuration and one corrupt archive ("Archive is not a ZIP
archive") red'd the leg, the aggregate Quality gate, and the
Advance-vpinned-marker job. The gh-1655 pre-install action now also
pre-installscmake;3.22.1with bounded retry + idempotent skip (same
hardening the NDK path already carries); the guard test pins the new
AC6 assertions and the AGP-version coupling (AC7). -
feat(#1750): per-model image caps — the outbound request never carries
more unique images than the target model accepts.Modelgains an
optionalmaxImages(catalog-seeded:gemini-4-argon: 10, measured);
images.perModel.<modelId>config overrides it (strictimages:
section, user truth wins); the registry resolves
perModel > catalog > images.maxPerRequest(first present wins, the
issue-#1554 shape). A Google-shapedtoo many images … maximum allowed for model <id> is <n>400 is classified (image_overflow.dart, the
overflow.ts twin), latches the learned cap for the (provider, model)
pair for the run, and re-sends ONCE through the registry at the
smaller cap — current-first-then-newest drops with the existing
never-silent notices naming the learned cap; a second refusal
surfaces exactly as today. JSONL is never rewritten;
images.registry: falsekeeps the byte-identical legacy shape
including the recovery-off behavior. -
feat(gh-1760): oversized user-message intake spills to a file instead of
blowing the context window. Any incoming user message — headless
-p/positional, TUI first/mid-session, steering/follow-up drain,
external inbox — whose text passes the new relative threshold (a
spills.userFractionof the live window budget minus the compaction
reserve, floored byspills.userThresholdbytes) is redacted and
written verbatim to.fah/spills/<sessionId>/user-<n>.md(the #678
layout) from one choke point (Agent.userMessageGuard); the model
receives a bounded head+tail preview plus a read-in-parts instruction
riding the replacement USER message (copy in
prompts/spill/user_read_instruction.md). Images stay inline, a
refused spill write degrades to today's inline behavior, and
spills.enabled: false/userThreshold: 0/ the new
FAH_USER_SPILL_THRESHOLDenv knob (bytes) keep the legacy shapes. -
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.