Commit Graph

81255 Commits

Author SHA1 Message Date
Peter Steinberger 8638d50ce6 fix(ci): cover core-driven extension impact in PR fallback (#124579)
* fix(ci): cover core-driven extension impact in the PR fallback plan

Fixes #124412

* fix(ci): self-gate extension inventory changes
2026-08-16 06:12:44 -07:00
Peter Steinberger 761203b7fe fix(compaction): bound the ratcheting file-operations summary lists (#124577)
formatFileOperations in agent-core joined every accumulated read/
modified path unbounded — and the lists ratchet: each compaction merges
the prior summary's file operations into the next accumulation, so a
long default-mode session (or branch summarization, which runs in every
mode) re-emitted an ever-growing path dump into each model-visible
summary with no cap, violating the context-budget invariant.

The safeguard extension already solved this with a bounded formatter
(900 chars/list, 2000 chars/section, '...and N more' overflow). Move
that implementation into the agent-core owner so compact(),
generateBranchSummary, and the safeguard all share one bounded
formatter, and delete the safeguard-local duplicate plus its constant
copies.
2026-08-16 06:11:39 -07:00
Peter Steinberger ec9135b22b fix(cli): five operator papercuts — actionable gateway/channel errors, legible status tables (#124581)
* fix(cli): render gateway connect failures as an actionable message

Gateway-required commands (e.g. `agents delete` with the gateway down)
printed a bare `Error: connect ECONNREFUSED 127.0.0.1:19801` with no next
step. Wrap raw socket-level connect failures at the callGateway owner into a
GatewayTransportError that names the target URL and points at
`openclaw gateway run` / `openclaw gateway status`; every gateway CLI
call site inherits the fix.

* fix(cli): point unknown-channel errors at channels list

`message send --channel qa-channel` and agent delivery failed with a bare
`Unknown channel: qa-channel`. Route both throw sites through the existing
formatUnknownChannelMessage helper so the error names
`openclaw channels list --all` as the next step.

* fix(cli): stop console redaction from garbling models status auth counts

The auth overview line used `(oauth=1, token=1, api_key=0)`; the console
secret redactor matched `token=`/`api_key=` as credential key=value pairs
and printed `token=*** api_key=*** |` with unbalanced parens. Switch to
count-first wording (`1 oauth, 1 token, 0 api-key`) via a shared formatter
and add a regression test that runs the line through redactSensitiveText.

* fix(cli): show recorded session totals in the sessions table

`openclaw sessions` rendered `unknown/200k (?%)` right after turns while
`openclaw status` showed real numbers: the table dropped any recorded
total without freshness provenance. Align with status semantics — always
show the recorded total, withhold only the percentage when the
freshness fact is missing.

* fix(cli): middle-truncate long plugin source paths in the list table

Out-of-root plugin source paths wrapped a single `plugins list` row across
5+ terminal lines. Middle-truncate the table fallback at 48 chars so both
path ends stay identifiable; verbose and JSON output keep the full
pasteable path.
2026-08-16 06:09:33 -07:00
Peter Steinberger 7ed321de30 fix(plugins): surface swallowed cleanup, setup, and CLI-load failures (#124570)
Three silent-failure sinks in the plugin lifecycle, all violating
'every action ends in a visible outcome or a recorded reason':

- cleanupReplacedPluginHostRegistry collects per-hook cleanup failures
  instead of throwing (it must finish every plugin), but both callers
  discarded the returned failures array entirely — broken session-
  extension/scheduler teardown vanished. The shared funnel now warns
  per failure with plugin and hook ids.
- setup-registry dropped broken setup entries with catch{return null}
  and threw-away registration errors with catch{return false}, silently
  removing a plugin's providers/CLI backends/migrations from
  onboarding. Both now push typed diagnostics (setup-entry-load-failed,
  setup-registration-failed) — and since the diagnostics array had no
  operator surface at all, the registry build now warns once per
  uncached build for every diagnostic (including the previously
  invisible descriptor-drift ones).
- loadPluginCliDescriptors swallowed total load failures behind a muted
  per-plugin logger; a failure removes every plugin command from
  help/dispatch and must not vanish with it. One warn on the catch.
2026-08-16 06:03:39 -07:00
Peter Steinberger 509f947a20 fix(ui): scope Labs mutations to connection (#124578) 2026-08-16 06:03:14 -07:00
Peter Steinberger 9f29ebc828 perf(test): reuse QA hook routing fixture (#124565)
* perf(test): reuse QA hook routing fixture

* test(qa): stabilize Matrix probe deadline assertion
2026-08-16 06:02:43 -07:00
Peter Steinberger 1787fd6e0a fix(macos): fully persist embedded CUA pid (#124572) 2026-08-16 06:01:53 -07:00
Peter Steinberger 0d9a424d81 refactor: dedupe session-catalog cursor paging and sleep helper clones (#124569)
* refactor(plugin-sdk): dedupe session-catalog cursor paging into session-catalog-runtime

The acpx Pi and opencode session catalogs carried byte-identical
boundedLimit/encodeCursor/decodeCursor/optionalRawCursor/transcriptPage
scaffolding (~114 duplicated lines each). Move one canonical copy into the
private-local session-catalog-runtime SDK subpath both plugins already may
import, and keep the plugin-named cursor guards as direct aliases.

Net -87 production LOC; no behavior change (error strings, cursor canonical
form, and byte budgets are unchanged).

* refactor(core): replace private sleep() clones with canonical sleep helpers

managed-linux readiness polling now uses @openclaw/retry sleepWithAbort with
ref:false (preserving the clone's timer.unref behavior), and the embedded
agent runner's async-task wait uses src/utils/sleep.ts. Both clones matched
canonical semantics on real inputs (positive integer poll intervals).

The update-managed-service-handoff copy stays: it lives inside a serialized
standalone handoff script (String.raw template) that cannot import repo
modules.
2026-08-16 05:57:42 -07:00
Peter Steinberger a118bfbf30 fix(slack): stop sniffing the NO_REPLY product token in the transport (#124561)
sendMessageSlack special-cased core's silent-reply token before any API
call, returning a fabricated 'suppressed' messageId. Silent-reply
stripping is owned by core auto-reply normalization before payloads
reach outbound — no sibling channel transport has this check, so a
literal NO_REPLY sent through the message tool delivered everywhere
except Slack. The check predates the extension extraction (it moved
verbatim in 8746362f5e) and is duplicate policy.

Delete the check, its sentinel mint (the only one in the repo), and the
receipt filter for it; tests now pin sibling-parity delivery instead of
the suppression.
2026-08-16 05:56:41 -07:00
Peter Steinberger 4e652f9fac fix(heartbeat): emit the documented quiet-hours skip event (#124542)
The active-hours guard returned skipped/quiet-hours without emitting a
heartbeat event, while every sibling skip in the same stage emits one.
'openclaw system heartbeat last' and the troubleshooting docs promise
reason=quiet-hours is observable; the silent return hid the window from
operators. Emit the same skipped event the siblings do.
2026-08-16 05:49:47 -07:00
Peter Steinberger 458923cb03 fix(gateway): preserve device capacity failures (#124574) 2026-08-16 05:48:39 -07:00
Peter Steinberger eab287d224 docs(gateway): correct node pairing storage (#124571) 2026-08-16 05:46:44 -07:00
Peter Steinberger ebd23631ae perf(cli): build root help from plugin metadata (#124562) 2026-08-16 05:43:35 -07:00
Peter Steinberger 7511601092 fix(gateway): omit JavaScript class names from proven RPC failures (#124563)
* fix(gateway): keep diagnostic formatting out of wire errors

* test(gateway): cover strict delivery wire errors

* test(gateway): complete strict delivery request fixture
2026-08-16 05:40:55 -07:00
Peter Steinberger e3e45ed462 fix(ui): preserve task recovery across refresh (#124371) 2026-08-16 05:39:57 -07:00
Peter Steinberger 91a3b9ba56 fix(cron): stop reaping deleted agents (#124566) 2026-08-16 05:38:14 -07:00
Peter Steinberger d437a4a4b4 fix(test): route extension roots through bounded planner (#124553)
* fix(test): route extension roots through bounded planner

* test: cover bounded Codex fallback shards
2026-08-16 05:35:17 -07:00
Peter Steinberger c1622d08f5 fix(qa): stop shutdown from leaving bus poll timers (#124463)
* fix(qa): settle bus polls during shutdown

* fix(qa): fence late bus waiters during shutdown

* fix(qa): reject writes after bus shutdown
2026-08-16 05:33:56 -07:00
Peter Steinberger f43544f752 refactor: remove vestigial runtime indirection (#124554)
* refactor: remove vestigial indirection

* test: update Slack runtime API guard
2026-08-16 05:31:36 -07:00
Peter Steinberger 66db70133b perf(test): avoid Canvas Git timeout wait (#124547) 2026-08-16 05:06:53 -07:00
Peter Steinberger 6e7b1e2215 fix(gateway): honor pairing exemptions on reconnect (#124545) 2026-08-16 05:05:11 -07:00
Peter Steinberger 23a034d584 fix(cli): scope unknown-subcommand help to its command tree (#124544) 2026-08-16 05:03:00 -07:00
Peter Steinberger bba57301d9 perf(ci): fold built-runtime verifiers into the artifact-check wave
build-artifacts is the wall's pole in 4 of the last 5 main runs (171-186s,
~15-20s ahead of the next lane), so its serial steps are the wall. The
Doctor plugin-index proof, singleton smoke, and startup-memory check ran
as their own 13s step even though they are independent dist readers that
the 47s artifact-check wave could absorb.

They now run inside that wave: on Blacksmith all seven start together, so
the verifiers cost the wave's max instead of 13s of serial time; hosted
runners still serialize the three through run_verifier so the RSS ceiling
measures an unloaded process. The step drops its selection gate because
the verifiers always run -- each artifact check already self-gates on its
own RUN_* flag, so a run with no checks selected still verifies.

Proof: extracted the step body and ran it with stubbed pnpm/node. Both
modes behave (Blacksmith 7 checks started, hosted-with-nothing-selected
still runs the 3 verifiers), and a failing verifier exits 1 with its
::error annotation in both -- the wave cannot swallow it.
2026-08-16 04:52:56 -07:00
Peter Steinberger dbad5e385d refactor(workers): separate runner consent from capacity (#124356)
* refactor(workers): separate runner inventory state

* docs(runners): track inventory cleanup

* test(workers): use bundle-only launch wire

* test(workers): update optional inventory fixture

* test(workers): validate prewarm inventory capability

* fix(workers): version runner inventory cutover
2026-08-16 04:46:38 -07:00
Peter Steinberger ee4bd4aa47 fix(scripts): give the artifact mtime repair a millisecond of headroom
checks-node-core-tooling-2 failed on main (run 31943910358) with
'expected 1786879225197 to be >= 1786879225197.2246': the repaired output
mtime landed a fraction of a millisecond below the input it had to clear.

isArtifactSetFresh repairs output mtimes to exactly ceil(newestInput), so
it leaves zero headroom for sub-millisecond write rounding or lagging
metadata on CI filesystems. When the repair lands at or below its input
the mtime fast path never engages, and every later invocation in that
checkout falls back to re-hashing every input byte -- the expensive path
this repair exists to avoid, gating the d.ts emit that is the slowest
build-all phase (25.9s of a 61s build).

Neither macOS APFS nor an idle Linux ext4 Testbox reproduces the
shortfall in 300 runs, so the repair now clears the newest input by a
whole millisecond instead of matching it. The assertion pins that
headroom, making the test deterministic where it was previously
load-dependent (fails 615 vs 616 without the fix).
2026-08-16 04:40:23 -07:00
Peter Steinberger 96cacfdf74 fix(gateway): batch worker placement session evidence (#124372)
* perf(gateway): batch worker placement session evidence

* test(gateway): tolerate elapsed workspace timeout

* fix(gateway): preserve unknown placement evidence

* fix(sessions): validate batched placement evidence

* fix(gateway): preserve incognito placement evidence

* fix(sessions): bound configured target discovery

* fix(gateway): canonicalize batched placement evidence
2026-08-16 04:28:06 -07:00
Peter Steinberger b07c6b2b8b fix(computer-use): macOS live-proof rig deadlocks on operator device approval (#124536)
* fix(computer-use): unblock the macOS live-rig proof flow

The rig ran its operator CLI and its proof runner from one state dir, so both
shared one device identity. A paired operator device is pinned to the scopes of
its first connect, and `nodes list` connects first for `node.pair.list`
(operator.pairing); the proof runner then needs operator.write, which is a scope
upgrade the gateway never approves silently and which no rig client can approve
for itself. The proof runner is a GATEWAY_CLIENT/BACKEND client, so on a
loopback auth-none gateway it is admitted unpaired with the scopes it asks for:
giving the CLI its own `cli-state` identity is enough, and `agent-state` now
never accumulates a pairing row.

`nodes list` also read `node.list` through the plain CLI client while
`nodes status`/`describe` used the diagnostics ladder. On any gateway where the
CLI must pair, the unfiltered list silently dropped connected/commands/
computerUse and `--connected` failed outright, so the documented rig gate could
not confirm the node. Both call sites now use `callNodeDiagnosticsGatewayCli`.

Docs drop the `devices approve <requestId>` instruction, which was circular:
that invocation is its own new device identity.

* test(cli): share the runtime-log formatter across nodes CLI e2e files

The extracted diagnostics-auth file stringified captured log arguments directly, which the type-aware core lint stripe rejects (no-base-to-string). Move the existing formatter into the shared node test helpers instead of duplicating it.
2026-08-16 04:25:03 -07:00
Peter Steinberger d86593fa28 fix(gateway): prevent sustained-turn readiness stalls (#124528)
* perf(gateway): reuse prepared plugin generations

* fix(agents): break prepared runtime import cycle

* chore: keep release notes in PR body

* test(agents): preserve plugin snapshot mock exports

* test(agents): consolidate plugin generation coverage
2026-08-16 04:20:46 -07:00
Peter Steinberger 0c546979b2 fix(release): exclude RoboClaw from contributor credit (#124526) 2026-08-16 03:19:26 -07:00
Peter Steinberger 15a01bcc5e fix(sessions): reject invalid explicit store paths (#124515)
* fix(sessions): validate operator-supplied store paths

* fix(sessions): preserve legacy store selectors

* style(sessions): avoid unchecked store assertions

* fix(sessions): keep store errors concise

* chore(sessions): document SQLite schema probe
2026-08-16 03:06:43 -07:00
Peter Steinberger 3f4dd7119f fix(cron): keep error class names out of run history (#124511)
* fix(cron): keep error class names out of run history

* fix(cron): preserve lifecycle abort reason

* fix(cron): preserve coded abort reasons

* docs: record cron abort follow-up
2026-08-16 03:03:23 -07:00
Peter Steinberger d5f41f734b fix(ci): trust QA tooling for frozen targets (#124509)
* fix(ci): trust QA tooling for frozen targets

* fix(ci): validate frozen QA tooling context

* fix(ci): verify frozen context without checkout

* fix(ci): pin QA tooling to workflow revision
2026-08-16 02:41:24 -07:00
Peter Steinberger 77902b185d fix(tasks): validate filter flags before querying (#124520) 2026-08-16 02:13:57 -07:00
Peter Steinberger 46f5951d53 fix(audit): reject filter combinations that cannot match (#124513) 2026-08-16 02:09:23 -07:00
Peter Steinberger e12079fb13 fix(gateway): keep readiness aligned with chat dispatch (#124514) 2026-08-16 01:59:08 -07:00
Peter Steinberger b9901605e8 fix(backup): report archive problems instead of raw errno text (#124510) 2026-08-16 01:46:23 -07:00
Peter Steinberger 32ddbb3be2 fix(ui): chat send failures were invisible when the session pane was hidden (#124473)
* fix(ui): always surface terminal chat send failures

Terminal chat send failures were recorded only on the queue item when the
owning pane was not visible (reconnect, alias drift, split-pane routing),
so the operator saw nothing at all — an action ending in silence.

Root cause: every error surface in the send/drain path was gated on
visibleSessionMatches(...), and the FIFO outbox drain treated every
chat.history reconcile rejection as a silent retryable "blocked", so a
non-retryable rejection (e.g. auth loss) wedged the head — and everything
behind it — permanently with no visible outcome.

Fix, at the owner:
- New surfaceChatDeliveryFailure() in steer-lifecycle.ts (the shared
  error-text owner): visible pane keeps the inline chat error; otherwise
  the failure routes through the existing global toast host, naming the
  session. All terminal failure sites in chat-send-delivery,
  chat-outbox-drain, chat-send-queue-state, and steer-lifecycle now use it.
- reconcileStoredChatOutboxHead: a non-retryable GatewayRequestError on the
  head now terminally fails a never-attempted head (unblocking the lane)
  or parks an attempted head as unconfirmed — both with a visible outcome —
  instead of blocking the lane forever.
- Composer disabled-reason: the reason now also renders while a draft hides
  the placeholder, and the cloud-startup-pending gate gets a reason instead
  of a silently disabled composer.

Regression tests: hidden-pane terminal failure surfaces via toast; wedged
head fails visibly and the lane drains the next message; attempted head
parks unconfirmed; disabled reason visible with draft text present.

* fix(ci): raise startup JS baseline for global failure surfacing and pin workspace-sync test clock

The chat send path now imports session-display naming for the global
failure toast, adding ~1 KiB gzip to startup JS (335452 B on CI's Linux
builder, still well under the 358400 B committed cap).

workspace-sync "never commands" asserted the exact dispatch timeoutMs
(777) but the impl derives it from a Date.now() deadline, so any elapsed
ms between admission and dispatch failed the exact-equality assertion on
a loaded runner. Pin the clock like the sibling timeout tests do.

* fix(ui): surface route-switched command failures and agent-scope global toast naming

ClawSweeper review findings on #124473:

- A queued local command failing after the operator navigated away hit
  failCommand(error) with expose=false; the dispatcher's stale-scope
  guard had already withheld the inline error, so a successful state
  write recorded the failure invisibly — the silent class this PR
  removes. Expose it globally when the scope is stale and the owning
  pane is hidden; a stale scope with the pane still visible keeps the
  failed queue chip (the new connection owns the inline surface).
- Global session rows are agent-scoped behind one shared "global" key,
  so the toast row lookup could borrow another agent's label. Match the
  row's agentId to the failed outbox's agent for global keys.

Both regression tests fail pre-fix (stash-verified).
2026-08-16 01:38:06 -07:00
Peter Steinberger b097aaab20 fix: doctor --fix reports config changes that were never written when validation refuses the candidate (#124462)
* fix(doctor): never report unpersisted config fixes

doctor --fix printed "Doctor changes" panels while computing candidate
mutations, then crashed with a raw Error and persisted nothing when the
repaired candidate still failed write validation (e.g. an unknown root
key repaired alongside an unrepairable schema type error).

Root cause: the compute->print->validate->persist ordering was wrong.
Panels were printed at mutation time, but validation only ran inside the
atomic writer, after all panels were visible.

Fix at the owner boundaries:
- doctor-config-flow queues repair-mode "Doctor changes" panels in a
  sink instead of printing them; preview panels still print immediately.
  Committed side-effect repair notes (SQLite/filesystem) keep printing
  at repair time; candidate-config notes from the repair sequence are
  routed through the same deferred sink.
- io.write throws a typed CONFIG_VALIDATION_FAILED error (with the full
  issue list) via a new createConfigValidationFailedError owner in
  io.write-errors.
- runWriteConfigHealth prints queued panels only after the atomic write
  commits, and renders a validation refusal as a "Doctor warnings" panel
  stating no config changes were written plus the exact paths to fix by
  hand. The contribution loop stops after refusal (same invariant as the
  cron-ownership deferral) and doctor exits 1 without a raw Error leak.

Regression tests: pre-fix, the new validation-refusal e2e test fails on
the lying "Doctor changes" panel; contribution-level tests cover the
refusal note, held panels printing exactly once after commit, and no
retry of the identical candidate.

* fix(doctor): report partial persistence accurately after a later write refusal

Post-rebase CI and ClawSweeper follow-ups:
- repair-sequencing tests now assert the deferred configChangeNotes contract
  (candidate-only mutation notes moved out of changeNotes by the parent
  commit); committed side-effect notes stay in changeNotes.
- formatConfigValidationFailure is module-private; its guidance formatting is
  covered through createConfigValidationFailedError, fixing the knip
  unused-export gate.
- When the initial write pass committed and only the later post-repair write
  is refused, the warnings panel says earlier fixes were saved instead of
  claiming no config changes were written, and the outro says "some config
  fixes were not applied". New regression test covers commit-then-refusal.
2026-08-16 01:12:43 -07:00
Peter Steinberger 6f418b93c6 fix(outbound): persist mirror route only after successful delivery (#124459)
A failed message send (e.g. missing channel credentials on a fresh
install) durably rewrote the agent's folded main session route before
delivery was even attempted: prepareOutboundMirrorRoute called
ensureOutboundSessionEntry pre-send, stamping delivery.route/origin and
minting a conversations-registry row for the never-reached target. The
Control UI then showed the phantom channel identity and the composer
bound to a dead conversation.

Route resolution stays read-only in prepareOutboundMirrorRoute; the
durable write commits once per send at the first success proof:
identified platform evidence (onDeliveryResult) or plugin action
acceptance (onPluginSendAccepted), both before the in-delivery
transcript mirror so first-contact routes still create their session
row, with a post-return safety net for adapters whose results carry no
platform identity. The gateway send RPC sibling had the same pre-send
persistence and gets the same commit-on-evidence ordering.

Regression test proves a failed send leaves the seeded main-session
origin and conversation identity untouched while a successful send
still persists the mirror route; fails on pre-fix code.
2026-08-16 00:49:18 -07:00
Peter Steinberger f19f62cb77 perf(node-host): prewarm worker bundles (#124427)
* perf(node-host): prewarm worker bundles

* test(gateway): gate worker finalization responsiveness

* fix(node-host): negotiate bundle prewarming

* fix(protocol): refresh worker prewarm models

* fix(node-host): preserve prewarm upgrade compatibility
2026-08-16 00:41:42 -07:00
Peter Steinberger a2e27de593 fix(agents): sweep leaked files at prepared-absent paths during agent deletion (#124461)
Gateway agents.delete closed the agent database (unlinking -wal/-shm)
during preparation, then awaited config/cron/session-purge work that
reopened the database and recreated the sidecars. The sweep classified
those recreated files as foreign replacements ("cleanup path appeared
after deletion preparation"), preserved them, cascaded ancestor
protection over the whole agent directory, and finished the deletion
journal with exit 0 while ~157 files survived under agents/<id>/.

The deletion journal fence already blocks legitimate claims beneath
prepared paths, so a file that appears at a prepared-absent path can
only be leaked deleted-agent state: adopt its identity and sweep it
instead of preserving it. Prepared-present paths keep the existing
identity-mismatch protection for genuine operator replacements.

Regression tests: recreated WAL sidecars between preparation and
cleanup are trashed with the agent directory, and recovery sweeps a
file that appeared at a journaled prepared-absent path.
2026-08-16 00:27:23 -07:00
Peter Steinberger 063ce57caf fix(gateway): cache prepared session targets (#124453) 2026-08-15 23:59:49 -07:00
Peter Steinberger 507e8b985e fix(cron): finalize removed direct runs (#124457)
* fix(cron): finalize removed direct runs

* refactor(cron): clarify terminal outcome ownership

* docs(qa): record cron receipt audit

* chore(qa): remove product-branch audit report
2026-08-15 23:51:40 -07:00
Jason (Json) bccbb8a0c7 fix(memory): make persistence promises receipt-backed (#120989)
* fix: require memory persistence receipts

* fix(memory): bind persistence failures to tool owners

* fix(memory): preserve Codex persistence ownership

* test(plugin-sdk): account for owner metadata export

* test(codex): split owner timeout regression

* fix(memory): cover durable deletes across runtimes

* test(codex): type dynamic mutation fixtures

* fix(agents): fail closed ambiguous side-effect owners

* fix(memory): fail zero-row delete receipts
2026-08-16 00:50:10 -06:00
Peter Steinberger 255ca8d96b fix(ci): trust frozen plugin prerelease fixtures (#124446)
* fix(ci): trust frozen plugin prerelease fixtures

* fix(ci): apply scoped plugin test omissions
2026-08-15 23:47:25 -07:00
Nehorai Hadad 7d05f7c5b9 fix(auto-reply): suppress fallback notices in rooms (#107209)
Hide model fallback and recovery notices in group and channel conversations while preserving direct-chat notices, persisted state, and lifecycle events.

Co-authored-by: NehoraiHadad <nehorai.hadad.projects@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-16 12:14:25 +05:30
Peter Steinberger 3554109801 fix(gateway): avoid stale session failure during model fallback (#124436)
* fix(gateway): defer provisional lifecycle errors

* fix(gateway): clear dormant observer retry timers

* fix(gateway): reuse observer timer cleanup on dispose

* refactor(agents): type lifecycle classifier input

* fix(agents): accept lifecycle payload records
2026-08-15 23:41:55 -07:00
Peter Steinberger a4407f638a fix(browser): reject ineffective Chrome MCP closes (#124449) 2026-08-15 23:31:27 -07:00
Aria 292ea7a338 fix(outbound): preserve angle-address emails in plain-text replies (#124249)
Preserve RFC 5322 angle-address emails without weakening namespaced tag stripping, and keep the iMessage security projection aligned.

Co-authored-by: Aria Ghasedi <drariaghasedi@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-16 06:24:22 +00:00
Peter Steinberger fa57fe5ae5 fix(ci): target sidebar reconnect roster response (#124430)
* test(ui): target selected reconnect roster request

* test(ui): decouple reconnect request scheduling

* test(ui): keep reconnect proof outcome-focused
2026-08-15 23:15:17 -07:00