flutter_agent_harness 1.0.652
flutter_agent_harness: ^1.0.652 copied to clipboard
Cross-platform AI agent harness for Dart and Flutter: streaming provider adapters, agent loop with tools, session persistence, context compaction.
Changelog #
Older entries: CHANGELOG_ARCHIVE.md — moved out
on 2026-10-09 (gh-1452): pub.dev server-rejects a publish whose
CHANGELOG.md exceeds its 262144-byte content cap, so this file keeps a
bounded recent window and scripts/check_changelog_size.sh fails fast when the cap is
approached. Since gh-1522 this file is CURATED-ONLY: add new lines under
## Unreleased and the release folds them into the tag's section at
publish-staging time — the published artifact's changelog is generated
(and trimmed under the cap by stamp_staged_release.sh), and the
publish job only WARNS on this repo file, so this file's size can never
block a release again.
1.0.652 #
-
fix(gh-1825): DAP agent enrollments carry a stable, independent
agentInstancethrough app/CLI bundles, persistence, direct boot and reconnect. Independent same-name agents keep distinct identities and stay online together, while legacy bundles retain their project-scoped identity. -
feat(gh-1825): DAP private-channel sends now use self-describing
fanet1frames (sender X25519 key in the frame, channel-bound AAD) viafa_hub_client0.2.11, so fa_network app members decrypt them without a whois/senderKey round-trip. Inbound channel frames openfanet1first and fall back to legacydap/1payloads. Verified live in both directions between a headless DAP agent and the Flutter app. -
feat(gh-1825): the network offline banner now has a Reconnect now action which restarts the socket and reloads the channel/member snapshot. Fresh sockets also clear a stale offline flag (the server sends no historical drain to a socket that was away during recovery).
AgentNetworkController.startstops after disposal instead of notifying a dead controller. -
feat(gh-1825): the roster sheet shows access-scope badges (whole network vs N channels, decoded additively from Member.access in snapshots and presence.changed) and owner/admins can drop an agent (DELETE /api/networks/{id}/agents/{agentId} with confirmation; the server tombstone keeps it out of future snapshots). Guests and store members are removable too via DELETE /api/networks/{id}/members/{id} (sessions revoked server-side); owner/admin/self rows stay protected.
-
feat(gh-1825): channel/network management in the app — rename a channel (PATCH), delete a channel or an owned network (REST DELETE with confirmation and state/navigation cleanup), and unnamed CLI/hub-created channels show a short friendly label instead of the raw 64-char id.
-
feat(gh-1825): pasting a DAP invite no longer demands a passphrase on macOS — importing NEW channel secrets auto-provisions a random passphrase into the per-channel Keychain item (decryption of existing envelopes never auto-generates; env/prompt/headless behavior unchanged), and passphrase warnings now name the concrete recovery (re-paste the invite to re-encrypt, or set DAP_KEY_PASSPHRASE).
-
feat(gh-1825): the network online counter opens a live-first roster sheet (names, member class, presence), and the channel composer offers roster-filtered @-mention autocomplete — the draft keeps the literal @displayName for DAP agents and the envelope carries the resolved member ids. On the CLI side the DAP channel notice names the agent's own identity (display name + agentId), and inbound channel mail that @-mentions that name (case-insensitive, word-boundary — no email or @name2 false positives) is marked "[mentions you — you are @name]".
-
fix(gh-1825): the app's network roster no longer reads as permanently empty ("0 online"): fa_network sends roster.snapshot with an OBJECT payload ({members: [...]}) and the app only accepted a bare list, so every snapshot silently decoded to zero members. Both shapes are now accepted, with a production-shape regression test. The DAP paste hint names web alongside CLI/macOS/iOS (plain-web join is supported).
-
fix(gh-1825): resumed CLI sessions rejoin their DAP channels and receive channel mail again — session replay now re-arms membership synchronization (plugin session-ready listeners on boot/resume and in-process switches) and idle channel probes complete deferred joins before inbox peek/drain. The post-replay sync also dials the membership's OWN hub with its enrolled credential before joining: a resumed session-scoped agent used to stay on the zero-config local URL forever, and because a disconnected fabric probe short-circuits before the sync, the remote dial never happened — the session-ready listener now drives it directly (throttled to one attempt per 10 s). App channel history no longer flashes raw payloads on first open: decoding waits for startup's channel listing instead of caching an undecoded guess until a manual switch.
-
feat(gh-1825): the fa app consumes the same DAP enrollment block as the CLI — paste it into the agent chat composer (macOS/iOS/Android; the smart-paste .txt chip path included) and the channel join happens without the block ever reaching
sendText, analytics, or session records. Native joins ride the live agent-fabric hub repository: per-project scoped identity, invite pub/priv import, keyless public channels, reconnect-persisted membership in a dedicated secure enrollment store (Keychain/Keystore where available). Harness additions:HubMessagingRepository.joinChannel/waitForConnectionwith rejoin-before-drain on reconnect, keyed channel decrypt (hubDecryptPayloadWithKeyPair), public showcase payloads, andHubBearerTransport(nativeAuthorizationheader or the browser?token=equivalent; authed dials never stringify transport errors into logs). Plain web joins through the browserWebHubTransport(?token=bearer equivalent, IndexedDB identity); the browser extension rides the service worker's existing DAP client (hub.joinop), never a panel-local second connection. Add agent now defaults to the DAP tab with a paste-into-chat hint; web gains ai-native provider sign-in through the existing popup/callback OAuth flow. -
fix(fa_network#4): the app inserts hub agents arriving via
presence.changedinto the roster (additive displayName/class fields) instead of dropping them, so the online counter no longer sticks at 0 when an agent comes online afterroster.snapshot; legacy frames without those fields create no phantom members.roster.snapshot/REST contents are logged for decisive presence debugging. -
feat(gh-1825):
/network+ scoped DAP channel membership — join by paste (invite url / url+keys / bare channel id / wholefa dap importcommand) with session/project/global scopes, live join without restart, per-scope leave + copy invite, and the composer-paste shortcut into the same wizard. Session scope rides adap_channel_membershipcustom record (leave tombstones, branch resume); project scope is.fah/dap.json(committable,dapkey1PBKDF2+ChaCha20-Poly1305 encrypted keys); global keeps the legacy~/.dapfiles. Headless CI:DAP_CHANNELS_JSON(+DAP_HUB_URL,DAP_CLIENT_SECRET,DAP_KEY_PASSPHRASE) joins at boot with zero prompts, plaintext or encrypted privs mixed. Passphrase chain: env → per-channel macOS Keychain item → one prompt with save default. -
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-slot
useMainConnection: trueopt-in (YAMLmodels.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/probedinput_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 in
prompts/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 persistedtask_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 dailybuild-macos.ymlbuild-extension leg) derives its tag as "latest+1 at its own dispatch moment"; on 2026-10-24 thev1.0.635tag was cut byauto_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.tsvwas 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; newtest/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's
textfor 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). Addsfa session clean [--dry-run], the/sessions+fa session listusage 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 asFA_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 logfa: 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's
Core 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.dartpins 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's
build-androidleg died at:jni:configureCMakeDebug[arm64-v8a]— the:jnimodule (transitivejnipackage via path_provider_android) wires externalNativeBuild.cmake with no version, so AGP auto-installed its defaultcmake;3.22.1mid-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 resolvesperModel > 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 (aspills.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 inprompts/spill/user_read_instruction.md). Images stay inline, a refused spill write degrades to today's inline behavior, andspills.enabled: false/userThreshold: 0/ the newFAH_USER_SPILL_THRESHOLDenv knob (bytes) keep the legacy shapes. -
fix(gh-1743): a structured compaction that hits its total-budget watchdog (the factory's 300s
totalBudget) 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.showCompactionDetailsopt-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 viacompact_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 existingcodex 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 omittedapiKeyEnvresolves the Keychain slot automatically (in-memory refresh), a newrefreshTokenEnv:field mints the initial access token on slot activation, and a raw Bearer token inapiKeyEnvis tolerated bychatgpt_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: new
exportAtifJsonwhole-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 anatif_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: asubmission: 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 inrunHeadless' 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_taskreturns 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 loudagent-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 dangling
model_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'susage-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 explicitagent.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 areanthropic/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: anthropicstill runs the checks). REG matrix (AC1/AC2/AC3) pinned intest/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 everysite/**or generator change. -
fix(cli): gh-1572 —
faboot no longer rewrites~/.fah/config.yamlfrom 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'sspills:,subagents:,jobs:,headless:,agent:,quota:,tui:,trajectory:,waiting:,power.holdandauto_updatesettings (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_diskPreservedSectionsdisk backstop covers every known top-level key.spills:renders intoYaml(newSpillsConfig.toYaml) andagent.misuseBreaker: falsepersists 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'sagent.misuseBreakerandagent.stuckToolkeys sofa config checknever false-fails on fa's own output; clearing the context cap drops only the cap key instead of the wholeagent:block (the siblingmode:/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_agentKeyValidatorstable (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/tokenexchange 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); newreadPasteboardText(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 newSessionTree.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 a
failledger 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 afailledger 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 to
emptyHiccupRetries(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 ininstall_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 byresult.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 exportFA_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 fixed
0.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), andinstall_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.
1.0.506 #
- fix(#1044): AIIN mobile/macOS add-provider — sign-in completes but the provider is never added (#1194)
- fix(ci): pass flutter-version explicitly — awf no longer defaults a toolchain (#1205)
1.0.507 #
- gh-1208 [BENCH] fix-git task image broken on CI runners — docker compose build fails in BOTH TBench-1 runs (unknown_agent_error, trial never starts) (#1218)
- gh-1206 WIP auto-save 2026-10-04T05-52-28 (#1216)
- gh-1209 [BENCH] usage fold skips failed trials — agent_timeout rows report 0/0 tokens though the session JSONL has full usage (#1123 follow-up) (#1215)
1.0.508 #
- fix(cli): gh-1459 — headless
fa -pno longer orphans live background shell jobs._awaitHeadlessBackgroundJobsdrained only in-flight subagent jobs, so a run whose model was still waiting on abash background:truecommand (gh-1440's full test suite) printed the detach summary and exited, killing the job with the container — the result it was explicitly waiting for never reached anyone. Live shell jobs now drain exactly like subagents: the settles ride the existing_onShellJobSettledfresh-turn path (steered into reaction runs the drain awaits, loop capped at 10 rounds), suppressed jobs (suppressSettleNotification— result already landed in-turn) are not awaited, and a settle landing outside a round still gets its reaction run awaited before exit. BOTH loops share ONE wall-clock ceiling, the newheadless:config section (shellJobDrainMs, default 30 min, strict parse;0restores the detach-immediately behavior) — a never-settling watch-loop detaches with an observable⏳ … drain ceiling … — detachingline instead of hanging headless, and the detach summary stays the documented degradation, never the default path. The drain prints a⏳ waiting: …line (#1055 parity) naming what the run stays alive for. While it waits, the drain also steers interim liveness notices (gh-1459 ask #4): everyheadless.shellJobQuietMs(default 5 min;0= off) of a still-running awaited shell job, ONE compact system-notice (job <id> running · <elapsed> elapsed · tail: …plus the bash_job output/stop escape hatch) keeps the model able to wait, inspect, or kill instead of blocking blindly to the ceiling; a crossing the model probed itself (bash_job status/outputbumps the entry's probe generation) is skipped, one steer budget per threshold, never a spam loop. - feat(providers): issue #1398 — stall-recovery tuning becomes per-provider
and data-driven. Registry entries (
customProviders:,models.custom:, roles chain entries,providersQueue:entries) accept optionalconnectTimeoutMs/streamIdleTimeoutMs(strict positive-int parsing); resolution is entry > theproviderTimeouts:section > the 180 s/300 s defaults, per field, and every seeded entry prints its effective values at boot. The wire seams (sendWatchedProviderRequestconnect watchdog,createSseIteratoridle watchdog) resolve per request URL, so all adapters inherit the tuning; runs without entries are byte-identical (AC6). Also: the retry backoff contract — a parseable delta-secondsRetry-Afterwins over the ladder for that attempt, hard-capped at the 60 s ceiling (the clamp names the advertised value;0counts as absent, malformed/HTTP-date headers fall back to the ladder with a trace note) — and the dual retry budgets (connection-class cap 4, stream-class cap 3, independent counters, the terminal story carries both). Trace lines pin the formatbudget=<connection|stream> attempt=<n>/<cap> delay=<ladder|server|clamp>; the tuning report prints per-model p50/p95 inter-chunk gap + watchdog-fire counts with thestreamIdleTimeoutMs ≈ 2× p95recipe (the production feeder lands with #1392's LatencyMeter). - feat(hosts): gh-1322 — the wireAgentCore host-adoption gaps: an embed
guide (
docs/embedding.md, with the explicit "hosts ride wireAgentCore, never adk_dart" rule), the host-facing key-slot resolver (AgentCoreServices.resolveKey+HostKeyResolver— the effective slot NAME, pinned-twin drift hints identical to the CLI's migration notes, registry-legknownSlotNames, injected env/store readers only), and lifecycle telemetry for in-process hosts (AgentCoreServices.telemetry: the pure interface + in-memory ring in core, the fa.log file sink behindlib/io.dart; requestStart/firstToken/turn/tool/run records with durations and the provider HTTP status). - chore(ci): document the sm-kicker head-completeness codeless-head crash
(upstream awf#15) — the factory job's
gh apicalls put--jqbefore--arg/--argjson, so a CODELESS branch head (2 parents, 0 changed files: a routine branch-sync merge with no content diff) crashes the job withaccepts 1 arg(s), received 4— even when CI is fully green on that head, and the job's own rescue dispatch can never heal exactly those heads. Workaround until the upstream fix: keep the branch head code-bearing. Live instance: run 37193754301 on head 1fe9b1232. - fix(messaging): gh-1180 —
schedule_messageself-chains no longer die silently at the agent-chatter wake cap. The idle inbox-wake gate (InboxWakePolicy, shared by the CLI and the app hosts) now exempts a delivered scheduled self-reminder ([scheduled]prefix, from == to) from the 10-consecutive-wake streak: a deliberate agent-chosen cadence (night watch, periodic sweep) wakes forever, while foreign agent-to-agent chatter stays capped (anti-storm REG) and user-kind mail always wakes. A 30s cadence floor bounds a zero-delay re-schedule spin (E5). Refused wakes are visible ([mail] wake refused, once per episode) and every lifecycle step is receipted to<messagesRoot>/_scheduled/receipts.jsonl(ScheduledReceiptLog): scheduled / delivered / delivery_failed / scan_failed / wake_attempted / turn_started / wake_refused. A failed scheduler scan (transientlistDirerror) now re-arms atfailureBackoffwith anonErrorlog instead of silently disarming the delivery heartbeat. - fix(messaging): gh-1180 review round — refusal receipts and the
visible refusal line are gated once per refusal EPISODE (the latch
lives in
InboxWakePolicynext to the streak and re-arms on the same resets), so a held gate no longer appends per-tickwake_attemptedrows (~43k duplicate lines/day) and a repeat episode after user input is announced and receipted again. The app host receipts its wake path (wake_attempted/turn_started/wake_refused) onto the same trail — AC4 is no longer CLI-only. A self-reminder adopted across a session-id change delivers FROM the live mailbox (from re-addressed alongside to), so the resumed chain stays self-shaped and exempt instead of falling back to the capped chatter lane. Plugin-only pending batches are explicitly classified as chatter (the contract is stated, not accidental), the wake cap is single-sourced fromInboxWakePolicy.defaultMaxInboxWakeStreak, and the policy file lost a stale copy-pastedignore_for_file.
1.0.509 #
- chore(pin): awf workflows track @main — always-latest policy (owner
directive 2026-10-04). All five machine-loop stubs (teammate, SM,
merge, sm-kicker, merge-trigger) flip from immutable SHA pins to
dmtools-agentic-workflows@main, so awf fixes (kicker real ticks awf#13, per-SHA concurrency awf#14, teammate defaults awf#12) go live on merge with no re-pin ceremony. Security trade-off (owner-accepted): a mutable ref executes whatever sits at the awf main head — mitigated by awf main being review-gated, bytest/machine_kit/factory_stub_ref_test.dartnow guarding ALL FIVEuses:refs resolve to exactlymain(never a stale SHA, never a random ref) in lockstep, and by the agents ENGINE pin staying an immutable 40-hex SHA viafactory_ref. Thesecrets:policy is settled per-callee in the same guard: teammate/sm/merge mapSOURCE_GITHUB_TOKENexplicitly (their factories declare it required), while merge-trigger keeps the factory-documentedsecrets: inheritPAT passthrough (factory-merge-trigger declares no secrets) and the kicker carries no secrets block. Stale immutable-SHA pin comments in ai-teammate.yml / machine-sm.yml rewritten to state the always-mainpolicy (#1222).
1.0.510 #
- gh-1226 [BUG] 1.0.505 (with #1190 aboard): mail-wake turn after /sessions restore still loses the provider/key binding — copilot-401 error on a z.ai session + fused guidance text (#1237)
- gh-1199 [GOAL] Deterministic-only Quality gate: mock LLM everywhere, live models move to supervised benches (#1229)
- feat(1101): Agent Wire Protocol v1 — web reference client (sdk/web), conformance runner, browser example (#1228)
- gh-1224 [BUG] iOS 1.0.504: all wasm interpreters (python/qjs/lua/sqlite3) dead — 'no lazy loader' in FOREGROUND bash _forJob() clone drops moduleLoader (regression suspect #1163) (#1225)
1.0.511 #
- gh-1164 [GOAL] built-in js-apps skill (promoted from app asset, decisive apps-vs-widget routing) + JS render/runtime errors reported back to the authoring agent as failures (blocked by #1151) (#1246)
- gh-1244 [FLAKE] PTY integration: secret_sheet_test «IT-mask: masked from the first keystroke» waits for exactly 5 bullets while typing races ahead (red main, run 37232401938) (#1245)
- gh-1241 [GOAL] Session token-usage ledger: resume-aware usage.json fold over the session chain (provider-reported, estimated marked) (#1243)
- ci(quarantine): skip scheduled_indicator_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1252) (#1253)
- gh-1077 [GOAL] flutter_app compaction parity with CLI — mobile sessions never compact (roles/smol absent, wrong context window, silent failure, no over-window relief) (#1239)
- ci(quarantine): skip job_board_stability_pty_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1250) (#1251)
- gh-1235 Wire jsr.openUrl + webView host into the app JS engine (js_widget_runtime = 0.4.154) (#1238)
- gh-1226 [BUG] 1.0.505 (with #1190 aboard): mail-wake turn after /sessions restore still loses the provider/key binding — copilot-401 error on a z.ai session + fused guidance text (#1237)
- gh-1199 [GOAL] Deterministic-only Quality gate: mock LLM everywhere, live models move to supervised benches (#1229)
- feat(1101): Agent Wire Protocol v1 — web reference client (sdk/web), conformance runner, browser example (#1228)
- gh-1224 [BUG] iOS 1.0.504: all wasm interpreters (python/qjs/lua/sqlite3) dead — 'no lazy loader' in FOREGROUND bash _forJob() clone drops moduleLoader (regression suspect #1163) (#1225)
1.0.513 #
- fix(1096): reseed fa-extension size floor for intentional js-apps skill growth (gh-1164) (#1258)
1.0.514 #
- fix(#1197): TUI frame pipeline self-heals a throwing view/render (gh-1197 AC3) — a mid-run render exception now logs loudly, invalidates the diff state (no stranded DEC 2026 BSU), and repaints instead of dying or freezing; PTY liveness regression test proving paced thinking + answer text + a long silent tool call paint continuously (AC1/AC2/AC4), via the scripted stream's new paced
chunks/pace_ms,thinking, andsleep_mssteps
1.0.515 #
- gh-1292 fa-tokens line must reach stdout in headless/CI runs (ledger emission invisible to the GH log) (#1295)
- gh-1296 [BUG] iOS build red in CocoaPods layer: package:sqlite3 build_hook fails — Podfile.lock drift (pods cache re-rolls weekly) never-again: ALL lockfiles committed + enforced (#1298)
- feat(1096): mac size diet — wasm_run_flutter.framework + never-loaded assets, ratcheted (#1227)
- chore(merge-trigger): track awf @main + map_pr_labels: true (#1285)
- ci(quarantine): skip secret_sheet_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1283) (#1284)
- fix(1280): CLI arch matrix gates the merge; release binaries boot-smoked (#1282)
- gh-1276 [BUG] iOS: memory_search returns garbage + memory_list hides user-scope notes (agent fell back to memory_delete by exact text) (#1281)
- ci(quarantine): test/integration/theme_readability_pty_test.dart (flake) (#1255)
- gh-1274 [BUG] iOS sandbox bash: relative paths resolve to a nonexistent CWD — files written in the sandbox are invisible to relative-path commands (still broken on 1.0.512 after #1225) (#1279)
- gh-1266 [BUG] nightly red: macOS flutter_app suite — 5-test cluster around apps panel / open_app launcher / dynamic_message tiles (suspects #1139, #1173) (#1278)
- gh-1275 [BUG] Built-in skills (#1151) not packaged into the apps: iOS ships EMPTY skill dirs (create-goal/fa-self-config/js-apps exist, SKILL.md missing) — verify macOS/Android too (#1277)
- gh-1272 [BUG] [URGENT] 1.0.512 TestFlight (build 205): EVERY JS widget dead — engine eval error 'Unterminated regular expression literal /[ ⇥' on all widgets (source corruption in assembly/catalog path) (#1273)
- gh-1265 [BUG] nightly red 5 nights: native desktop builds broken by a TRANSITIVE flutter_inappwebview nobody declares — linux WPE missing + windows MSVC 14.51 STL1011 (lockfile not committed) (#1268)
- gh-1261 [BUG] Play listing deploy fails: clear_images! HTTP 400 — invalid image_type "images" for en-US/phoneScreenshots (daily-publish Play leg red) (#1264)
- feat(#1079): SDK slice 2 — live agent-stack wiring (wireAgentCore); CLI converts to the shared builder (#1230)
- gh-1197 [BUG] TUI fixes (recovered dev leg) (#1263)
- ci(quarantine): skip dap_wake_hang_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1248) (#1249)
- gh-1073 Session JSONL grows unboundedly (12 GiB ledger records) → resume fails with 'Failed to read session' + heap-exhaustion crash (#1247)
- fix(1096): reseed fa-extension size floor for intentional js-apps skill growth (gh-1164) (#1258)
1.0.516 #
- gh-1299 [BUG] [URGENT] main red 13 legs: release commit v1.0.515 bumps version WITHOUT regenerating pubspec.lock — new --enforce-lockfile (#1268) hard-fails every leg (#1304)
1.0.517 #
- gh-1299 [BUG] [URGENT] main red 13 legs: release commit v1.0.515 bumps version WITHOUT regenerating pubspec.lock — new --enforce-lockfile (#1268) hard-fails every leg (#1304)
1.0.518 #
- gh-1307 [BUG] nightly macOS suite red: js_app_error_capture_test — load error NOT captured (empty list) regression from #1273's engine fix, merged green through the PR exclusion-zone hole (#1309)
- fix(918): omp_ref reference fixtures captured on PTY host + nerd-glyph parity wiring (#1231)
- gh-1310 [CI] Tag CI for v1.0.513 red — 4+ jobs fail (Android APK, web debug, Hostile ambient env, PTY screenshots) → release asset-less, legs fall back to v1.0.511 (#1312)
- gh-1303 [CI] Pages 'Build landing + web demo' red on main — '41 packages have newer versions incompatible with dependency constraints' (blocks publish + merge-trigger) (#1305)
- gh-1299 [BUG] [URGENT] main red 13 legs: release commit v1.0.515 bumps version WITHOUT regenerating pubspec.lock — new --enforce-lockfile (#1268) hard-fails every leg (#1304)
1.0.520 #
- fix(1250): assert frozen-row invariant, not an unsampleable mid-drain frame (#1286)
- gh-1300 [ENH] PTY integration tests: pay toolchain+boot ONCE per shard, not per test (~30s spawn overhead × N tests) — boot-once + reset between tests (owner design), FA_BIN AOT seam as the cheap first cut (#1306)
1.0.521 #
- fix(1248): dart_tui guard misfires ghost mail-wake turns — log-and-keep-running (#1289)
- gh-1257 [FLAKE] PTY integration: convert the 5 residual exact-bullet-count anchors (secret_sheet_residual_test / password_prompt_pty_test) to the gh-1244 property anchor (#1287)
1.0.522 #
- fix(ci): play edit-commit sets changesNotSentForReview (#1367)
- fix(1339): surface recovered trials, terminal failure modes, and model ids in the bench summary (#1351)
- feat(1331): iOS size diet — IPA dSYM strip + never-loaded fixture prune + ratchet (#1333)
1.0.523 #
- gh-1341 Bump js_widget_runtime pin to 9d57570 / ^0.4.156 — flutter_js hostCall/capture channel fix (fa-craft lag, voxel-sandbox spinner) (#1366)
- fix(1349): deny bare long foreground sleeps; enrich liveness reminders with the background hint (#1355)
- fix(1293): dap start exit-0 guarantees the dial credential is durably on disk (#1354)
1.0.524 #
- ci(quarantine): skip shell_job_countdown_pty_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1365) (#1347)
- fix(1368): pub.dev publish — push-tag-only OIDC path, dart-only gate, verify waits instead of false-alarming (#1370)
- fix(1339): review hardening — session-archive traversal guard on every branch (#1369)
- fix(1323): live thinking streams in the TUI — reasoning deltas commit the transient-retry attempt (#1325)
1.0.525 #
- ci(quarantine): skip subagent_integration_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1372) (#1373)
- fix(1254): quarantine dracula theme-flake + wait headroom for starved shard (#1288)
- fix(1234): whole-tree CRAP ratchet 12→8, loc-gate migration, all top offenders fixed (#1291)
- fix(1252): widen scheduled-indicator reminder window past loaded-runner pre-check (#1290)
1.0.526 #
- feat(1322): wireAgentCore host-adoption gaps — embed guide, host key resolver, lifecycle telemetry (#1327)
- fix(1319): route paging history notifies through the guarded bridge (follow-up to #1320) (#1324)
1.0.527 #
- gh-1393 [GOAL] Mobile agent parity: bash-identical sandbox shell (glob/grep/cd/dev-null) + first-class jsr widget testing (jsr.test.*, check_app) (#1401)
1.0.528 #
- feat(1379): Compaction 2.0 second tier — agent-initiated hide, LRU re-hide, per-segment pins (#1383)
- fix(1402): pre-flight Play listing language gate + edit-discard on commit failure (#1405)
- feat(1380): obligations ledger substrate — verbatim entries + level-0 context block (A1 slice 1) (#1382)
1.0.529 #
- feat(1377): auto_update tri-state flag — notify banner, autonomous verified self-update, /update (#1384)
1.0.531 #
- fix(1406): bench ConnTrace dark inside tmux — launch-line env + loud-empty guard (#1416)
1.0.532 #
- ci(quarantine): skip pty_resume_equivalence_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1422) (#1423)
- ci(quarantine): skip composer_echo_pty_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1413) (#1414)
1.0.533 #
- gh-1412 [GOAL] Near-miss elimination: FinalizeGate contract + TaskLedger — the agent verifies produced state against task text before declaring done (#1418)
- gh-1403 [daily-publish] testflight leg failed (#1424)
- feat(bench): per-task test-budget override table + planner fairness guard (gh-1407) (#1417)
1.0.534 #
- fix(1408): harness-induced bench failures — isolated job logs, redacted at rest, shape-only bash interceptor (AC1–AC3; AC4 documented) (#1410)
- gh-1415 [GOAL] Subagent status line-language: one compact live row per subagent in the CLI TUI (omp-density, ten named deltas) (#1420)
1.0.535 #
- gh-1426 [GOAL] Model capability negotiation (rework): the resolver's loud
notes now ride the built
Model(capabilityNotes) and render on the status surfaces —/model-editprints them next to the resolved triple, and the single-model boot prints them in the boot notice — so a gated pin (E3), an override-vs-endpoint divergence (E1), and a catalog-miss override (E4) are never silent. Also:applicationNote('models')says "applies live to the next model build" (the flow live-applies; "next boot" was wrong), the duplicate_capabilitySummaryForcollapsed onto_capabilitySummary, the two capability remove paths share one_removeCapabilityYamlbody, the omit-max-output menu row names its adapter scope ("openai-completions only"), the deadresolveModelRefCapabilitiesexport is dropped, and the resolver's ceiling-table lookup and documented wire field both read the same effective api (api ?? spec?.api). - Behavior change (gh-1426, deliberate — AC6): pre-existing
thinkingLevelpins (aroles:chain entry, theFA_PROVIDER_CONFIGpreconfig, or amodels.overridespin) now reach the wire for the first time on the openai-completions family (reasoning_effort) and google (thinkingConfig); onorigin/mainthey were carried but wire-silent there. Consequences: (1) REG-1's "no overrides → byte-identical payload" corpus holds only for sessions with NO thinking pin at all — a session with a bare roles pin changes payloads by design; (2) an endpoint that rejectsreasoning_effort(strict gateways, o-series rung spelling) can now 400 where it previously worked — unpin the level (/settings → Model capabilities → thinking level → off) or pinomitMaxOutputTokenswhere the max-output field is the problem. The boot note that claimed the openai-completions adapter "is not wired" to the config-carried level is corrected and now fires only for the genuinely unwired adapters (dial, copilot, chatgpt-codex).
1.0.537 #
- feat(1374): animated two-tone kaomoji thinking indicator — app + web TUI (#1419)
1.0.538 #
- gh-1433 [GOAL] Workflow log fidelity: non-interactive headless renders EVERYTHING the model says — thinking deltas AND assistant text, default-on (the post-hoc log IS the UI) (#1437)
- gh-1425 [GOAL] Resume loses structured-compaction folds — marathon session reopens at 143% of window make fold projection resume-equivalent and cap every detonation path (#1427)
- gh-1430 [GOAL] Headless stream-liveness: fa must heartbeat provably-alive reasoning streams, and the bench stall-gap must defer to fa's own watchdog (round-4 RCA: 13 tasks ≈ 16% killed mid-thinking) (#1436)
- chore(1431): reseed fa-extension size baseline for the 3.47.7 engine rev (#1432)
- gh-1409 [GOAL] Compaction-pinned skill operative lines — instructions must survive folding (image-carrier precedent) (#1428)
- feat(1374): animated two-tone kaomoji thinking indicator — app + web TUI (#1419)
1.0.539 #
- gh-1438 bash_job: resolve near-miss/stale job ids instead of 'unknown background job' dead-ends bound status output GC exited jobs (#1447)
1.0.540 #
- gh-1441 Voxel nodes render as the “Voxel world” placeholder — Fa app builds JsonWidgetRenderer without voxelWorld (fa-craft 0.2.18, voxel-sandbox 1.0.0) (#1445)
1.0.541 #
- feat(1079): SDK slice 6 — IT-5 record-level runtime parity for extension hosts (YoClip scenario) (#1456)
- gh-1449 [GOAL] Orphan-result notices: reported once, never a user turn, always identifies the call (#1451)
- gh-1439 [GOAL] Live-follow etiquette: scrolling up while the agent works never yanks the user back down (TUI + app + web: follow-mode contract + «jump to live» affordance) (#1443)
1.0.542 #
- gh-1450 [GOAL] SSO/OAuth login must always show the full authorization URL and allow opting out of the default browser (CodeMie/OpenRouter/ChatGPT family) (#1464)
1.0.543 #
- gh-1450 [GOAL] SSO/OAuth login must always show the full authorization URL and allow opting out of the default browser (CodeMie/OpenRouter/ChatGPT family) (#1464)
1.0.545 #
- ci(quarantine): skip header_badge_pty_test.dart in the gate (flake, https://github.com/IstiN/flutter_agent_harness/issues/1469) (#1470)
1.0.546 #
- chore(factory): flip engine pin to agents-rel-20261009-181034 (206a8c62) (#1474)
- gh-1477 [BUG] build-mobile Derive version: fetch-depth:0 checkout outgrew its own 10-min timeout — daily leg cancelled mid-fetch (time bomb, grows with every tag) (#1482)
- gh-1444 [GOAL] iOS sandbox ergonomics: skills resolve, networks fail loudly, secrets prove presence, timeouts tell the truth (#1453)
1.0.547 #
- gh-1446 [GOAL] TUI chrome diet: textless fold rule, running/lost-only jobs line, live activity animation in the reserved _ Fa brand slot (#1454)
- fix(1431): size gate survives Flutter rev-hash churn — normalize 20+hex rev dirs, vendored carve-out, exact-patch SDK pin (#1486)
- gh-1471 [GOAL] bench.yml: provider selection for tbench runs (zai-glm default, kimi-for-coding) with a dedicated bench key (#1475)
- gh-1452 Release blocked: CHANGELOG.md exceeds pub.dev 256 KiB cap (262 672 B on main) — v1.0.538 not on pub.dev trim + pre-flight guard needed (#1462)
1.0.548 #
- gh-1478 [ENH] daily-publish auto-filer: signal-first failure excerpts + cancelled-run digests + root-cause dedup (never-again for noise filings) (#1480)
1.0.549 #
- chore: pin factory_ref to agents-rel-20261010-170624 (fb32c3c8) — brings gh-843 ghost fix + gh-840 sweep to the fa tick; live: #1457 ghost #2 BLOCKED with green-on-head (previously cost ~2h) (#1513)
1.0.550 #
- chore: pin factory_ref to agents-rel-20261011-002034 (aa5ddd8bd78e64c9ce77a43b31bc63b40b0f8d7a) — minter fix (gh-848/#849): gh-748 never cancels pre-materialization; unblocks fa #1457/#1520 (ghost-seized) (#1527)
1.0.551 #
- gh-1459 [BUG] Headless exits with live background shell jobs (detached, results lost) — must drain+steer like subagents (gh-1440 run orphaned its full-suite job) (#1463)