All four QA Smoke profiles fail on main since the #112880 ratchet landed:
startup JS gzip ~323,326-323,342 B vs baseline 322,526 + 512 B tolerance.
The overage comes from UI commits that landed after the baseline was
captured (#112859#112866#112853#111861). Local macOS builds measure
~322,535 B; Linux CI is ~800 B larger, so the baseline must track the CI
measurement (max observed across profiles, run 29980596056).
The startup JS gzip gate was a fixed ceiling that main drifted to within
1-3 KiB of, forcing repeated hand-bumps (315->317 KiB) and failing
feature PRs on drift they did not cause (#112649 burned two CI rounds).
check-control-ui-performance.mjs now also enforces a committed baseline
(config/control-ui-startup-budget-baseline.json) with a 512 B tolerance:
regressions beyond baseline+tolerance fail with an actionable message,
intentional increases become a reviewed one-line diff via
--update-baseline --reason, meaningfully lighter builds print a
lower-the-baseline hint, and a malformed/missing baseline fails closed.
The fixed ceiling stays as the cumulative-creep backstop.
Closes#112743.
* refactor(ai): invert plugin coupling behind the transport host port
* fix(ai): queue custom transport registrations until the host is configured
* refactor(ai): remove relocated transport sources from src/agents
* fix(ai): source core stream types from canonical packages and fix tarball fixtures
* fix(ai): invert plugin transport host wiring
* fix(ai): harden managed transport projection
* test(ai): register synchronous stream in transport mock
* fix(ai): lazily install transport runtime host
* fix(ai): preserve completion compat detection
Final producer batch of the media-placeholder program: Telegram primary
bodies are caption-only with one aligned structured fact per native
media; the message-cache kind-parsing regex is deleted (native kind
stored directly); reply-chain, debounce/forward, group-history, and
ambient transcript lines render structured facts via the shared
formatter (removing the plugin-local duplicate of the caption-less
literal); audio-transcript and sticker-description replacements gate on
structured facts instead of exact placeholder strings. Also restores
unconditional failed-retryable recording when media resolution is
aborted for live updates, so shutdown cannot silently settle an
undispatched update.
* feat(system-agent): agentic cached caretaker greeting with quick actions
* test(gateway): pin caretaker greeting in session-ownership tests
* fix(system-agent): offer agent handoff chip only when a model is available
* fix(ci): typecheck narrowing, lint, and knip gates for greeting module
* fix(system-agent): reject structured greeting output on every retained line
* test(gateway): split caretaker welcome tests under the max-lines gate
* fix(cron): support current and session: targets in initial delivery resolution
resolveInitialCronDelivery only checked sessionTarget === 'isolated' for
default announce delivery. All other cron paths (delivery-plan.ts:106-114,
normalize.ts:680-684, jobs.ts:288-298) already treat 'isolated', 'current',
and 'session:' as equivalent. This gap meant cron jobs created with
--session-target current or --session-target session:... would not get
default announce delivery at creation time.
Real behavior proof:
Input: sessionTarget='current', payload.kind='agentTurn'
OLD: undefined <- missing default announce delivery
NEW: {mode:'announce'} <- consistent with other cron code paths
Co-Authored-By: Claude <noreply@anthropic.com>
* test(cron): add typed initial delivery resolution tests for all session targets
* test(cron): document direct-service delivery contract and cover service callers
* refactor(cron): centralize detached-delivery-target predicate into shared helper
Extract isDetachedDeliveryTarget(sessionTarget, payloadKind) in normalize.ts
and use it from all three callers:
- normalizeCronJobInput (write-time default, normalize.ts)
- resolveCronDeliveryPlan (read-time default, delivery-plan.ts)
- resolveInitialCronDelivery (service-bypass default, initial-delivery.ts)
This removes the third manually synchronized copy that the original PR
added, addressing ClawSweeper P1 finding.
* fix(cron): align initial delivery targets
Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>
* test(cron): consolidate initial delivery coverage
Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>
* test(cron): keep delivery matrix type-safe
Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>
* chore: keep release note in PR metadata
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* refactor(ui): split lobster-pet into look/plans/element modules
Move existing behavior along appearance and seeded visit-planning ownership seams without changing runtime behavior.
Extract only the bounded logo stand-in, WebAudio chirp, and pure render-template helpers while keeping the state machine in the element. Remove the grandfathered max-lines suppression and baseline entry.
* style(ui): format lobster-pet split modules
* fix(ui): keep lobster split internals unexported, refresh i18n raw-copy baseline
knip flags the split's newly exported internals (strangerLookFor,
renderCrabSvg, lobsterPetSpriteStyle, LobsterPasserKind) — they have no
external consumers; the html-text raw-copy baseline is file-keyed, so the
sprite z/Z markup moving into lobster-pet-look.ts needs a regen.
* fix(ui): satisfy lint for the lobster-pet split (param reassign, max-lines)
* fix(ui): keep moved act-profile internals unexported
* refactor(ui): restructure Settings IA around user-facing pages
Settings sidebar shrinks to settings-only destinations grouped by attention:
General/Appearance/Notifications up top, Connections, Agents & Tools,
Privacy & Security, System. Sessions, Worktrees, Activity, and Memory
Import leave the takeover: Sessions/Activity return to the workspace
sidebar, Worktrees becomes a Sessions hub tab, Memory Import is reached
from the Agents memory panel. Model Setup becomes a Models subpage.
New config pages: Notifications (web push), Privacy & Security (curated
policy rows + security/approvals schema sections), Advanced (uncurated
schema sections + raw editor, replacing the General quick/advanced
toggle). General slims to model defaults, language, and gateway host;
identity moves to Profile, lobster appearance rows to Appearance.
* feat(agents): widen system-agent config writes to operator parity
Every config surface the Control UI can edit is now agent-writable behind
the exact-operation human approval gate, except a small documented
denylist ($include, auth, env, models, secrets). tools.* and
plugins.entries.* writes are allowed; plugin_uninstall works for plugins
that provably do not back the active default inference route (fail closed
otherwise); set_default_model takes an optional agentId and live-tests a
per-agent model with the same rigor as the default route. Per-agent
routing fields on non-default agents are direct-writable; the default
agent's route keeps the verified set_default_model/onboard paths. A new
config-write-parity contract test forces explicit classification of every
top-level config key.
* feat(ui): sync operator display prefs through config ui.prefs
Theme, theme mode, text scale, locale, and chat display prefs get a
canonical server-side home in config ui.prefs. The Control UI mirrors
them into localStorage for instant boot, writes local changes through to
the gateway (best-effort; viewer-scope or offline clients stay
device-local), and applies server-side deltas on connect and on every
config snapshot reload — so an agent changing the theme through the
approval gate takes effect in the UI, and prefs follow the operator
across devices.
* chore: prune quick.ts from max-lines baseline after slim-down
* style: oxfmt changed files
* chore: regenerate plugin-sdk api baseline for ui.prefs config surface
* fix: dedupe runPluginUninstall dep type and narrow locale extraction
* docs: update Control UI settings structure and ui.prefs sync
* fix: address autoreview findings on parity guards and prefs sync
- plugins.entries.* config writes get the same active-route ownership
check as plugin_uninstall (fail closed when unprovable)
- system-agent prompt now describes the guarded write policy instead of
the old blanket refusals
- per-agent set_default_model logs the agent-scoped label
- legacy /config?section=... deep links redirect to the Advanced page
- prefs sync: pushes coalesce and drain serially (no CAS races dropping
updates), replaced config hashes mark pre-patch snapshots as stale, and
the last-seen server value persists per gateway scope so reloads cannot
revert unpushable viewer-scope edits
- lint: theme membership checks use Sets
* fix: harden uninstall route guard and scope prefs queue per gateway
- plugin uninstall ownership now covers execution owners (runtime policy,
embedded harness override) in addition to the provider, and re-verifies
inside the commit boundary so a concurrent route switch cannot slip a
destructive uninstall past the pre-approval check
- prefs push queue binds to one gateway client; switching gateways drops
the old queue (device-local fallback) instead of cross-writing prefs
* style: top-level type import in config-page test
* fix: close review races in config-write guards and prefs reconciliation
- config_set/config_set_ref re-verify the inference-route guard inside
the commit boundary so conditional verdicts (per-agent routing, plugin
entries) cannot be flipped by a concurrent edit after approval
- server prefs apply per changed field, so a server change to one pref
no longer reverts unpushable local edits of the others
- replaced-hash staleness retires once post-patch state is observed, so
a genuine restore of the pre-patch config becomes authoritative again
- moved-section deep links (communications notifications, automation
approvals) redirect to their new pages
* style: satisfy no-useless-assignment and loop-condition lints in prefs sync
* fix(ci): resolve unhandled profile-test rejections and knip unused exports
The profile page's new identity section reads context.config; the test
context mock now provides it, eliminating the unhandled rejections that
failed checks-ui and the compact-large shard. Exports flagged by the
dependency gate (extract/patch helpers, section-key and prop types) go
module-private; extraction coverage moves behind applyServerUiPrefs.
* refactor(agents): move config-write policy to its own module
The static path classifier and documented denylist move to
config-write-policy.ts, shared by the execution guard and the parity
contract test — no test-only exports left for the dependency gate to
flag. Blocked-root refusals now cite the denylist's documented
escalation for that root.
* fix(acp): persist spawn label to target session store instead of requester store
When /acp spawn --label <name> creates a cross-agent ACP session (e.g.
from main into cursor or codex), the label was written to the requester's
store partition rather than the spawned session's store. The write was a
silent no-op because the session key (agent:cursor:acp:<uuid>) did not
exist in the requester's store. Follow-up commands like /focus <label>
and /acp status <label> then failed with "No session found with label".
persistSpawnedSessionLabel now resolves the canonical store path from the
spawned session key via resolveSessionStorePathForAcp before writing, and
only mutates the in-memory requester session store when both stores are
actually the same.
Fixes#106136.
* fix(acp): export resolveSessionStorePathForAcp for cross-store label persistence
* refactor(acp): extract spawn binding helpers to bindings.ts
* fix(acp): remove unused oxlint-disable after extracting spawn bindings
* chore(acp): prune stale max-lines baseline entry for lifecycle.ts
* test(acp): tighten cross-store spawn label coverage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* perf(sqlite): gate schema version before integrity scans and quarantine terminal open failures
* feat(gateway): refuse incompatible database schemas at boot and verify integrity in the background
* feat(update): refuse installing builds that cannot open the current databases
* docs: add database schema reference with version history and downgrade guidance
* test(update): tolerate optional runner options in git-mutation mocks
* refactor(update): split npm package-target metadata out of update-check
* chore: model verifier internals for knip and regenerate docs map
* chore(i18n): resync stale iOS localization catalog
* chore: allowlist read-only preflight and verifier raw SQLite access