Commit Graph

70153 Commits

Author SHA1 Message Date
Peter Steinberger
a4ed1eb083 fix: show cloud-worker progress in sessions tail (#110460)
* fix: persist cloud-worker session trajectories

* chore: remove release-owned changelog edit
2026-07-18 07:19:08 +01:00
xingzhou
75982be9a3 fix(cli): oversized secrets plans consume unbounded memory (#109652)
* fix(cli): reject oversized secrets plan files

* fix(cli): keep generated secrets plans applyable

* fix(cli): reject special plan files without blocking

* docs(cli): refresh secrets plan documentation map
2026-07-18 07:18:21 +01:00
dwc1997
ec3eb573bf test(shared): add unit tests for numeric coercion helper (#98663)
* test(shared): add unit tests for numeric coercion helper

* test(shared): cover numeric coercion boundaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 07:17:34 +01:00
Peter Steinberger
e5ac755681 fix(i18n): regenerate derived app catalogs inside native:i18n:sync (#110454)
Rewriting apps/.i18n/native-source.json without regenerating the derived
Android/Apple artifacts lands a stale iOS catalog and turns the repo-wide
apple/android i18n checks red for every PR (this is how main broke after
recent app-string additions: the inventory and translations were
committed while Localizable.xcstrings was not regenerated).

native-app-i18n.ts sync --write now chains syncAndroidAppI18n and a new
exported syncAppleAppI18n (catalog + InfoPlist strings) so one command
leaves the whole i18n tree consistent. The shared locale list moves to
scripts/native-i18n-locales.ts to break the import cycle between the
inventory scanner's top-level CLI await and the derived generators.
2026-07-18 07:15:41 +01:00
dwc1997
0d822a3495 test(cli): add unit tests for CLI argument quoting helper (#98567)
* test(cli): add unit tests for CLI argument quoting helper

* ci: trigger re-review

* ci: trigger re-review

* ci: trigger re-review

* ci: retrigger

* fix: correct empty string assertion in quoteCliArg test

* ci: trigger re-review

* ci: trigger re-review

* test(cli): add shell metacharacter tests for quoteCliArg

* test(cli): expand argument quoting coverage

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 07:15:37 +01:00
Peter Steinberger
616a657ac6 fix(ui): center session title on the native macOS titlebar centerline (#110462)
The Mac app injects a 52px transparent titlebar, so native chrome centers
at y=26 while the 48px pane header centered its content at y=24, leaving
the session title/chip/actions 2px high. Native desktop shells now size
.chat-pane__header from --openclaw-native-titlebar-height (same 50px
fallback as .macos-titlebar-controls).
2026-07-18 07:15:21 +01:00
Peter Steinberger
c237d00e44 fix(agents): keep Mythos context direct-only (#110461) 2026-07-18 07:14:43 +01:00
Peter Lee
8539640abc fix(config,secrets): emit JSON errors and cover dotenv roots (#104340)
* fix(secrets): use resolveStateDir for .env path to cover legacy .clawdbot dir

* fix(secrets): replace override-based legacy .env test with true automatic fallback, add apply-path coverage

* fix(config-cli): emit JSON error on config get --json for missing paths

When  is used with a nonexistent
path, the command emits no JSON on stdout, only a text error on stderr.
This breaks automated consumers that expect machine-readable errors.

Fix: output  JSON to stdout before exit(1) when
opts.json is set. This matches the existing JSON error output pattern
already used by runConfigValidate and handleConfigMutationError.

Also re-throw synthetic exit errors in the catch block so the
--json branch's writeRuntimeJson+exit is not caught and re-emitted
as a text error.

* fix(config-cli): use typed ExitError signal instead of __exit__ string sentinel

* fix(secrets): reuse resolved stateDir for .env path in audit

* fix(secrets): pass resolved stateDir into apply .env scrubbing, add root-switch regression

* test(config-cli): migrate remaining __exit__ sentinel to ExitError

* fix(secrets): cover config and state dotenv roots

* style(secrets): format dotenv path helper

* test(secrets): track temporary directories

* test(config): expect typed exit for missing patch files

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
2026-07-17 23:14:05 -07:00
Shakker
e613fd31bb docs: document Codex runtime availability checks (#110447) 2026-07-18 07:13:45 +01:00
Shakker
cc62bc7e0b fix: report unavailable Codex runtime in model status 2026-07-18 07:13:45 +01:00
Shakker
564a384f25 fix: resolve agent harness plugin availability 2026-07-18 07:13:45 +01:00
Shakker
006d6556b8 refactor: expose selected plugin payload checks 2026-07-18 07:13:45 +01:00
Peter Steinberger
deddeb3dac feat(sqlite): move quarantine decisions to a dedicated store that survives primary database damage (#110453) 2026-07-18 07:13:43 +01:00
xingzhou
713b6151d1 fix(qa): preserve crabbox command output unicode (#110278)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 07:13:25 +01:00
Peter Steinberger
5bfd03b845 refactor(gateway): simplify node connection state (#110452) 2026-07-18 07:13:11 +01:00
dwc1997
021d2526f0 fix(mentions): support single-char CJK names in mention pattern matching (#89864)
* fix(mentions): support single-char CJK names in mention pattern matching

The \b word boundary assertion does not recognise CJK characters as word
characters in JavaScript regex, so single-char CJK mention names (e.g.
@包) failed to match in group chat messages. Replace \b with explicit
lookaround assertions that treat CJK characters as boundary-worthy when
the mention pattern contains CJK characters.

Fixes #87303

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(tests): pass agentId to buildMentionRegexes in CJK mention tests

The test config uses agents.default.identity.name but was calling
buildMentionRegexes(cfg, undefined). Without an agentId, resolveAgentConfig
is never called and the identity is not resolved, producing empty regexes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(tests): use agents.list config shape for CJK mention tests

Use the canonical agents.list array config shape instead of the
non-existent agents.default object. Pass the matching agent id to
buildMentionRegexes so resolveAgentConfig finds the identity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(mentions): match derived names with Unicode boundaries

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 07:12:53 +01:00
xingzhou
ab99029b3b test(ui): stabilize browser initialization checks (#110403) 2026-07-18 07:10:47 +01:00
Peter Steinberger
3a570e70aa fix(ui): move child-session toggle after the session title so parent rows align to the sidebar grid (#110459) 2026-07-18 07:10:37 +01:00
Peter Steinberger
eea38748be test(ui): cover plan-snapshot replay on reconnect end to end (#110091)
* test(ui): cover plan-snapshot replay on reconnect end to end

QA follow-up to the plan-replay work: the mock gateway scenario can now
serve an inFlightRun snapshot plus session run state through chat.history
and chat.startup, and a Playwright suite proves the checklist seeds from
the replayed snapshot on load, is replaced in place by a newer live plan
event, clears on the terminal outcome, and survives a mid-run page
reload. Registered as QA scenario control-ui-plan-replay-reconnect.

* test(ui): satisfy plan replay e2e lint
2026-07-18 07:10:15 +01:00
dwc1997
8b03d5ce24 fix(elevenlabs): reject malformed base URL overrides (#105163)
* fix(elevenlabs): fall back to default base URL when config value is malformed

* fix(elevenlabs): reject malformed and non-http(s) base URL overrides

* fix(elevenlabs): redact configured URL from base URL validation errors

* fix(elevenlabs): preserve realtime WebSocket overrides

* test(elevenlabs): avoid stale realtime test overlap

* style(elevenlabs): format realtime URL assertion

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 07:08:45 +01:00
Peter Steinberger
2612728b3b fix(ci): feed candidate identity JSON to validator (#110456) 2026-07-18 07:07:00 +01:00
pick-cat
99046179a8 fix(vydra): bound stalled generated image download body reads (#109210)
* fix(vydra): bound dripping asset downloads with a wall-clock deadline

* fix(vydra): bound dripping non-2xx error bodies with wall-clock deadline

* fix(vydra): preserve bounded provider error metadata

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 07:05:07 +01:00
Peter Steinberger
723f5ba4fc improve(ui): restructure Settings around user-facing pages with full agent parity (#110445)
* 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.
2026-07-18 07:03:31 +01:00
Peter Steinberger
8992f92dab feat(gateway): idempotent start, non-interactive stop guard, unified lifecycle audit (#110323)
* feat: guard gateway lifecycle mutations

* fix: harden gateway lifecycle mutation guards

* fix: harden gateway lifecycle audit safety

* fix(cli): repair gateway lifecycle checks

* fix(cli): satisfy lifecycle CI gates
2026-07-18 07:01:38 +01:00
Peter Steinberger
8ae1581798 feat(clients): provider-grouped model picker with fast, verbosity, and inherited-thinking controls (#110339)
* feat(clients): provider-grouped model picker with fast, verbosity, and inherited-thinking controls

* chore(i18n): translate model-control strings and regenerate platform catalogs

* feat(clients): inherited-default restore for fast mode and verbosity controls

* fix(clients): preserve control state in thinking snapshots and persist inherited verbosity

* chore(i18n): resync native inventory after rebase onto message-reader main

* chore(i18n): realign locale artifacts after rebase onto message-reader main

* refactor(clients): drop superseded fast-mode accessors and picker convenience flagged by periphery

* chore(i18n): resync inventory line refs after dead-code removal
2026-07-18 07:01:31 +01:00
xingzhou
46101687e0 fix(ui): long chats retain generated image memory (#110305)
* fix(ui): bound managed image blob cache

* test(ui): prove managed image cache eviction

* fix(ui): reopen evicted managed images safely

Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>

* fix(ui): preserve deferred image preview handle

Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 07:00:15 +01:00
Peter Steinberger
26bca9990d feat(ui): add X (Twitter) link to About page community links (#110449)
* feat(ui): add X (Twitter) link to About page community links

* test(ui): cover X link in About view href assertions
2026-07-18 06:59:49 +01:00
xingzhou
66990b207f fix(ci): bound CLI installer smoke curl with connect and max-time (#109927)
* fix(ci): bound CLI installer smoke curl with connect and max-time

The non-root CLI installer smoke pipes curl directly into bash under a
single docker-run timeout. curl has no default connect or total timeout,
so a stalled CDN connect or hung response body could pin the container
until the outer docker-run budget kills it. Bound the curl with the same
--connect-timeout 30 --max-time 300 shape already used by install-sh-smoke
and install-sh-e2e, and enable pipefail so curl failures are not masked
by the bash consumer.

* test(ci): cover bounded CLI installer pipeline

* fix(ci): bound preceding non-root installer download

* fix(ci): harden bounded installer URL handoff

Co-authored-by: zhang-guiping <zhang.guiping@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 06:59:29 +01:00
mushuiyu886
8178cf1038 fix(nostr): preserve reply event context (#110085) 2026-07-18 06:57:31 +01:00
DaigoSoup
7a61f728bf fix(update): keep gateway healthy during concurrent updates (#108925)
* fix(update): prevent concurrent managed handoffs

* test(update): type managed handoff mock

* fix(update): reject concurrent handoff continuations

* refactor(update): clarify managed handoff ownership

---------

Co-authored-by: Josh Lehman <josh@martian.engineering>
2026-07-17 22:56:09 -07:00
Peter Lee
d1163dac16 fix(acp): persist spawn label to target session store instead of requester store (#106241)
* 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>
2026-07-18 06:55:44 +01:00
Peter Steinberger
442c166122 refactor(sessions): replace ambient watch sentinels with provenance (#110428)
* refactor(sessions): store watch provenance in cursors

* style(docs): align database schema table

* refactor(state): use Kysely for watch migration

* style(state): remove redundant timestamp casts
2026-07-18 06:55:29 +01:00
Peter Lee
734920c513 fix(config): restore env vars after any load failure (#106181)
* fix(config): restore env mutations before rethrowing any config load error

* test(config): tighten env rollback coverage

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 06:53:32 +01:00
tzy-17
931ac3e2b5 fix(infra): return undefined for bigint exceeding MAX_SAFE_INTEGER in normalizeSqliteNumber (#106081)
* fix(infra): return undefined for bigint exceeding MAX_SAFE_INTEGER in normalizeSqliteNumber

Number(bigint) silently loses precision for values beyond
Number.MAX_SAFE_INTEGER (2^53-1) and returns Infinity for extremely
large values. Downstream code comparing these values against thresholds
(e.g. expiration timestamps, byte counts) could make incorrect decisions.
Return undefined instead so callers can handle the out-of-range case
explicitly.

* fix(sqlite): harden bigint normalization proof

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 06:52:10 +01:00
Peter Steinberger
679f9fcc68 fix(ui): move sidebar section chevrons after labels, show counts only when collapsed (#110448) 2026-07-18 06:50:50 +01:00
Peter Steinberger
ab0277946d fix(qa): isolate active memory migration (#110358) 2026-07-18 06:50:13 +01:00
Peter Lee
cafa7606b7 fix(cli): show clean errors for missing input files (#105874)
* fix(cli): wrap readFileSync ENOENT with user-friendly messages and suppress cause leak

* fix(secrets): type missing plan errors

* style(secrets): format missing plan error branch

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 06:45:58 +01:00
Peter Steinberger
bd4714a042 fix(ui): give settings drill-in chevrons currentColor stroke so they stay visible in dark mode (#110446) 2026-07-18 06:45:48 +01:00
Peter Steinberger
538a3826c7 fix: preserve active turns during session compaction (#110389) 2026-07-18 06:45:09 +01:00
MatthewSynthia
c23dc147b8 fix: launchd reload handoff strands gateway when restart races the drain window (#110213)
* fix(daemon): reload handoff outwaits gateway drain before re-bootstrapping

The launchd reload handoff waited only 15 x 0.2s (~3s) after bootout for the
label to unload, but the booted-out gateway keeps the label registered until
its drain-before-exit window (up to 300s) completes. Bootstrap then failed
with EIO mid-drain, and the kickstart -k fallback cannot succeed on a
booted-out label, leaving the LaunchAgent deregistered and the gateway down
until a manual bootstrap.

Extend the post-bootout wait to cover the full restart-deferral budget plus
margin (315 x 1s, derived from DEFAULT_RESTART_DEFERRAL_TIMEOUT_MS), and
replace the single dead kickstart fallback with a bootstrap retry loop that
only falls back to kickstart -k while the label is actually registered, so
the handoff never exits with the service deregistered.

Closes #110137

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(daemon): scale reload bootout wait to the effective drain budget

A config-raised gateway.reload.deferralTimeoutMs would outlast the fixed
default-derived wait and reopen the stranded-LaunchAgent race. Thread the
effective deferral timeout from restartLaunchAgent into the handoff and derive
the reload bootout wait from it; unbounded (<=0) configs keep the finite
default wait, with the bootstrap retry loop covering the overshoot.

Related: #110137

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(daemon): derive reload wait from launchd ExitTimeOut and keep failed bootstrap status

Review follow-up: the bootout SIGTERM path is bounded by the plist's
ExitTimeOut, not the gateway's restart-deferral config, so derive the reload
bootout wait from LAUNCH_AGENT_EXIT_TIMEOUT_SECONDS plus margin and drop the
deferralTimeoutMs threading. Capture a failed bootstrap's status in the else
branch: after a completed if with a false condition $? is 0, so exhausted
retries logged 'restart done' and exit 0 while the LaunchAgent stayed
deregistered. Adds an execution-level retry-exhaustion test that runs the
generated script against an always-failing launchctl stub.

Related: #110137

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(daemon): exercise delayed launchd reload handoff

Run the generated reload handoff through delayed-stop and exhausted-bootstrap
paths, and clarify that the wait covers launchd's ExitTimeOut stop window.

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

* test(daemon): narrow generated handoff script

Fail clearly when the spawn arguments omit the generated script and pass a
narrowed string to the execution helper.

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

* fix(daemon): retry bootstrap after launchd kickstart race

Continue the bootstrap retry loop when a label disappears between the
registration check and kickstart, and cover the race by executing the generated
handoff script.

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

* test(daemon): make handoff no-wait sentinel explicit

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

---------

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 06:44:02 +01:00
Peter Steinberger
d4d23fe954 fix(mattermost): preserve websocket posts across restarts (#110386)
* fix(mattermost): adopt durable ingress drain at the websocket chokepoint

Posted events processed detached from the websocket receive with only a
5-minute in-memory guard; a crash lost the post and reconnect never replays
it. Raw posted envelopes now journal durably (event_id = post.id per the
upstream Post model, lane per channel_id, one row per post) before handler
scheduling; dispatch runs through the core drain with deferred claims through
debounce, merged-flush fan-out adoption, gated-turn settlement, and 30d/20k
tombstones covering the old 5min/2k guard, which is deleted after its parity
test. post_edited stays excluded and cannot be swallowed by posted
tombstones. Cold-gap limitation stated: Mattermost cannot replay posts missed
while disconnected.

Autoreview blocked by codex sandbox network in the build stage; full manual
review performed (updated one websocket test asserting the pre-adoption
parsed-post contract to the raw-envelope contract). Part of #109657 wave 2.

* style(mattermost): keep ingress monitor type internal

* fix(mattermost): retry then loudly escalate a failed durable append

Landing autoreview caught a real loss path: a durable enqueue failure at the
websocket chokepoint was logged and swallowed — the raw envelope discarded,
the connection kept running against a broken store, and reconnect never
replays, so a transient SQLite failure silently lost the post. The append now
retries with short backoff for transient blips; a persistent failure
propagates and the websocket terminates loudly so the outage is
operator-visible instead of silently dropping every subsequent post.
Regression test covers both the absorbed-transient and escalation paths.

* style(mattermost): format rebased ingress handler

* docs(mattermost): document bounded auth-failure retries under deferred claims

* fix(mattermost): guard the drain pump against stop racing the async prune

stop() disposing before the startup pump finished pruning let the pump
lazily create a fresh undisposed drain and dispatch after shutdown. The pump
now re-checks running after the prune, and stop() disposes again after
awaiting the pump so a drain created mid-race is torn down. Regression test
blocks the prune across stop and asserts no dispatch.

* fix(mattermost): serialize durable admissions to preserve lane order

Concurrent websocket callbacks let a post in append-retry backoff be
overtaken by its successor, inverting same-channel arrival order in the
queue. Admissions now chain (order over latency, mirroring the iMessage
admission tail); regression proves a retried post still lands ahead of a
concurrently received one.

* test(mattermost): assert lane order via dispatch sequence

* fix(mattermost): stop() awaits in-flight admissions before disposal

* fix(mattermost): satisfy ingress lint checks

* fix(mattermost): honor envelope-level channel ids in the durable inspector

Posts can carry their channel id on the post, the event data, or the
broadcast envelope — the monitor dispatch honors all three, but the ingress
inspector and claim-side validator required the nested field, rejecting valid
posts as permanent and (via the storage-failure escalation) tearing down the
socket for a failure that never happened, losing posts reconnect cannot
replay. Both sites accept the three shapes; regression proves an
envelope-level post dispatches.
2026-07-18 06:43:27 +01:00
Peter Steinberger
746d257f05 feat: contract sessions-family tool outputs (#110424)
* feat(agents): contract sessions-family tool outputs

* refactor(agents): remove unused fast-mode exports
2026-07-18 06:43:12 +01:00
Shakker
f448701aa1 test: align changed-target routing expectations (#110422) 2026-07-18 06:42:27 +01:00
Shakker
e5fed572ef test: exercise metadata-driven watch exclusion (#110422) 2026-07-18 06:42:27 +01:00
Shakker
75ceb56d71 fix: preserve generated host bundle exclusions 2026-07-18 06:42:27 +01:00
Shakker
3fd448c559 fix: keep browser asset build package-local 2026-07-18 06:42:27 +01:00
Shakker
309a97bb3d fix: refresh generated paths before watch restarts (#110422) 2026-07-18 06:42:27 +01:00
Shakker
643476f24d fix: derive generated asset watch exclusions 2026-07-18 06:42:27 +01:00
Shakker
b118e9e913 fix: preserve generated asset mtimes 2026-07-18 06:42:27 +01:00
Peter Lee
893196483d fix(anthropic): add Fable 5 to Claude CLI catalog (#101453)
* fix(anthropic): add claude-fable-5 to CLI allowlist, aliases, labels, and context window

CLI path was missing claude-fable-5 across all four metadata layers while
the direct Anthropic provider path in register.runtime.ts already had full
support via isAnthropicFable5Model() / resolveAnthropicFixedContextWindow().

- CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS: add claude-cli/claude-fable-5
- CLAUDE_CLI_MODEL_ALIASES: add fable/fable-5/claude-fable-5 mappings
- CLAUDE_CLI_CONTEXT_WINDOWS: add claude-fable-5 -> 1_000_000 (was falling back to 200K)
- CLAUDE_CLI_MODEL_LABELS: add Claude Fable 5 (Claude CLI)
- resolveClaudeCliImageMediaInput: add fable-5 to 2576 max-side tier

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(anthropic): route fable alias through context/model-ref canonicalization paths

* fix(agent): include claude-cli in fable-5/mythos-5 fixed context window resolution

* fix(anthropic): add claude-fable-5 to static CLI manifest catalog

* test(anthropic): cover fable-5 alias canonicalization in bare and provider-qualified forms

* style(anthropic): fix indentation in plugin manifest

* fix(anthropic): publish Fable CLI output limit

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-17 22:36:45 -07:00