Commit Graph

6827 Commits

Author SHA1 Message Date
Isla-Liu bef8fbd3b6 fix: align wakeup notice with message rail 2026-07-09 03:21:44 +00:00
Isla-Liu d1345e6ed0 fix: handle process wakeup attachments 2026-07-09 03:21:44 +00:00
Isla-Liu fa04044e7c fix:[WebUI] 修正 background wakeup 訊息渲染邊界 2026-07-09 03:21:44 +00:00
nesquena-hermes 432f596cfb Merge pull request #5814 from nesquena/release/stage-5762
Release: derive service tier from agent fast-mode metadata (#5762)
v0.51.939
2026-07-08 20:21:11 -07:00
nesquena-hermes dd3ffda0b6 docs(changelog): #5762 service-tier from agent metadata 2026-07-09 03:17:25 +00:00
Hermes Agent 69d0fc73c9 fix: derive service tier support from agent fast-mode metadata 2026-07-09 03:16:53 +00:00
nesquena-hermes 4111e097c8 Merge pull request #5813 from nesquena/release/stage-5773
Release: keep live reasoning on one renderer path (#5773)
v0.51.938
2026-07-08 20:16:18 -07:00
nesquena-hermes 1d3cf5028b docs(changelog): #5773 single reasoning renderer path 2026-07-09 03:13:03 +00:00
Rod Boev e592498664 fix(chat): keep live reasoning on one renderer path (#5720) 2026-07-09 03:12:37 +00:00
nesquena-hermes 45575f588a Merge pull request #5812 from nesquena/release/stage-5770
Release: contain streaming code block layout (#5770)
v0.51.937
2026-07-08 20:12:05 -07:00
nesquena-hermes 7b6a23f902 docs(changelog): #5770 code-block containment 2026-07-09 03:08:48 +00:00
Rod Boev 837a552746 fix(chat): contain streaming code block layout (#5760) 2026-07-09 03:08:19 +00:00
nesquena-hermes 862a121b24 Merge pull request #5811 from nesquena/release/stage-5721
Release: workspace switcher screen reader accessibility (#5721)
v0.51.936
2026-07-08 19:46:24 -07:00
nesquena-hermes db7b146de9 docs(changelog): #5721 workspace switcher a11y 2026-07-09 02:41:05 +00:00
sheldon-im 67f8414656 Keep closed workspace dropdown out of SR navigation 2026-07-09 02:40:36 +00:00
sheldon-im 06e0df4656 Announce New Chat workspace transiently 2026-07-09 02:40:36 +00:00
sheldon-im 23de53c004 Improve workspace switcher screen reader state 2026-07-09 02:40:35 +00:00
nesquena-hermes d6e6e91de4 Merge pull request #5810 from nesquena/release/stage-5729
Release: keep child streams from moving parent sidebar bucket (#5729)
v0.51.935
2026-07-08 19:40:04 -07:00
nesquena-hermes e0a23ba0f5 docs(changelog): #5729 sidebar child-stream bucket 2026-07-09 02:36:20 +00:00
Rod Boev 745e0be0df fix(#5699): keep child streams from moving parent sidebar buckets 2026-07-09 02:35:52 +00:00
nesquena-hermes 522e90b5b8 Merge pull request #5809 from nesquena/release/stage-5786
Release: handle terminal output EventSource transport errors (#5786)
v0.51.934
2026-07-08 19:35:21 -07:00
nesquena-hermes 6c8944450c docs(changelog): #5786 terminal EventSource error recovery 2026-07-09 02:26:49 +00:00
ai-ag2026 d05f3e4b7d Handle terminal output EventSource transport errors
The composer terminal's output EventSource registered handlers for
output/terminal_closed/terminal_error but none for the transport-level 'error'
event, so a session expiry, killed gateway, or network drop left the terminal
frozen with no feedback and no recordClientSSEError telemetry.

Add an 'error' handler behind the existing identity guard: let the browser
auto-reconnect a CONNECTING source (one-time "reconnecting" note, no manual
loop/backoff), and on a permanently CLOSED source surface a "disconnected" line
and dispose it (null TERMINAL_UI.source) so a restart can reconnect. Notify once
per outage to avoid flooding the pane/telemetry on a flapping connection. Emits
recordClientSSEError('terminal', {ready_state, reason}) like the chat/session
consumers.

UI-evidence (screenshot of terminal on gateway-kill) still needed before merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 02:26:20 +00:00
nesquena-hermes 9765f0e45b Merge pull request #5808 from nesquena/release/stage-5761
Release: refresh OpenCode Go static catalog (#5761)
v0.51.933
2026-07-08 18:28:43 -07:00
nesquena-hermes 5f2ec9e1f0 docs(changelog): #5761 OpenCode Go catalog refresh 2026-07-09 01:23:33 +00:00
sbe27 68d406f7ff fix(models): refresh OpenCode Go static catalog 2026-07-09 01:23:08 +00:00
nesquena-hermes f8f60d5fdd Merge pull request #5807 from nesquena/release/stage-5782
Release: back off bg drain loop instead of silent tight-spin (#5782)
v0.51.932
2026-07-08 18:22:36 -07:00
nesquena-hermes a1aca345e1 docs(changelog): #5782 drain-loop backoff 2026-07-09 01:18:50 +00:00
ai-ag2026 141b49b74c test(bg): fix drain-loop backoff test import so it actually collects
The regression test used a bare `from _wakeup_helpers import install_fake_registry`,
which raises ModuleNotFoundError under the repo test runner (rootdir = repo root,
tests is a package). pytest treats that as a COLLECTION error for the file, not a
failure — so the whole suite stayed green while this file's tests silently never
ran, leaving the backoff fix with zero effective coverage.

Use the package-qualified `from tests._wakeup_helpers import install_fake_registry`,
matching the four sibling bg_task_complete suites. Confirmed the import now
resolves from the repo root (the bare form fails).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:18:21 +00:00
ai-ag2026 8dde1de4fa fix(bg): back off drain loop instead of silent tight-spin (#2476, #4633)
_drain_loop read process_registry.completion_queue directly under a bare
`except Exception: continue`. A registry missing that attribute raised
AttributeError that was swallowed and retried with no backoff — a
100%-CPU tight loop with no log line. The loop now reads the queue via
getattr(..., None) and backs off on the stop event when it is absent
(mirroring streaming.py), catches queue.Empty explicitly for the normal
idle path, and logs a warning + backs off (_DRAIN_STOP.wait(1.0)) on any
other queue error so a persistent fault can neither spin the CPU nor stay
invisible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:18:21 +00:00
nesquena-hermes 8e00277ce5 Merge pull request #5805 from nesquena/release/stage-5785
Release: frame passkey-login 200 with Content-Length (#5785)
v0.51.931
2026-07-08 18:10:07 -07:00
nesquena-hermes 80276850a6 docs(changelog): #5785 passkey-login Content-Length 2026-07-09 01:05:56 +00:00
ai-ag2026 b10116bffc Frame passkey-login 200 response with Content-Length
The passkey-login success handler wrote its 200 JSON body without a
Content-Length header, unlike the password-login and logout handlers. Under
HTTP/1.1 keep-alive that response is unframed, so the browser's fetch().json()
waits for connection close and appears to hang until it times out.

Encode the body once and send Content-Length before end_headers(), mirroring
the /api/auth/login block. Header order is unchanged so set_auth_cookie's
Set-Cookie still precedes end_headers().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:05:28 +00:00
nesquena-hermes 9dacbe60de Merge pull request #5804 from nesquena/release/stage-5787
Release: pair metering begin_session with end_session teardown (#5787)
v0.51.930
2026-07-08 17:55:24 -07:00
nesquena-hermes 2c46e9e251 docs(changelog): #5787 metering session begin/end pairing 2026-07-09 00:51:59 +00:00
ai-ag2026 4fc51072a2 fix(streaming): pair metering begin_session with end_session teardown (#4633, #2476)
The streaming worker called meter().begin_session(stream_id) but never a
paired end_session. A turn that produced zero output tokens (pre-flight
cancel, or a setup exception before the first token) left a _SessionMeter
in GlobalMeter._sessions forever: get_stats() only prunes sessions with
first_token_ts > 0, so a zero-token session is never reclaimed, and each
leaked entry inflates the `active` count sent over the SSE metering event.

begin_session() and the metering ticker's .start() are now registered as
the first statements inside the worker's outer `try`, and paired with an
idempotent meter().end_session(stream_id, 0) plus a deterministic
_metering_stop.set() in that try's outer `finally` (the same block that
pops STREAMS/CANCEL_FLAGS) — so every exit path tears the session down.
Deferring .start() until after put() is defined also closes a latent
start-before-put ordering window in the ticker closure.

The metering payload is unchanged; end_session only pops the session.

Verification: tests/test_metering_session_lifecycle.py (leak reproduction,
end_session reclaim, idempotency, begin->cancel-before-token->empty) plus a
plain-assert run on python3.11 (conftest gates pytest to 3.11-3.13; system
python is 3.14). py_compile clean.

WARNING streaming-contract: adjusts the streaming worker's begin/end
teardown ordering. Needs RFC/contract review against docs/rfcs/
(session-sse-contract-v1.md) before merge — draft PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 00:51:28 +00:00
nesquena-hermes 4fc637d8d0 Merge pull request #5790 from nesquena/feat/update-channels
Release channels: stable / experimental self-updater
v0.51.929
2026-07-08 17:12:50 -07:00
nesquena-hermes d4120b47b5 test(updates): update timeout-guard assertions for the channel-threaded apply/force bodies 2026-07-08 22:52:53 +00:00
nesquena-hermes 236a2c8032 fix(updates): thread offered channel through apply/force to close the debounce race
Codex gate CORE: /api/updates/apply + /force ignored the request-body channel
and re-read the saved setting, so a channel switch whose debounced autosave
hadn't landed could apply the OLD channel. The banner now sends the channel the
CHECK reported for each target; the endpoints validate it against the enum and
thread it into apply_update/apply_force_update (None → saved-setting fallback,
preserving prior behavior). Agent stays channel-neutral server-side.
2026-07-08 22:42:54 +00:00
g 912c626195 Merge remote-tracking branch 'origin/master' into pr-5790-live 2026-07-08 22:26:22 +00:00
nesquena-hermes 3730a4a551 Merge pull request #5796 from nesquena/release/stage-5784
Release: evict run-journal writer-lock cache on journal delete (#5784)
v0.51.928
2026-07-08 14:23:09 -07:00
Nathan Esquenazi be004baaaf Merge branch 'master' into feat/update-channels 2026-07-08 14:20:48 -07:00
nesquena-hermes 37adf8b7ce docs(changelog): #5784 evict run-journal writer-lock cache on delete 2026-07-08 21:14:04 +00:00
ai-ag2026 c5b15850e5 Fix run-journal writer-lock cache leak on session delete
`_lock_for` caches one threading.Lock per (dir, file, pid) in the module-global
`_WRITER_LOCKS`, but nothing evicted those entries: `delete_run_journal` rmtree'd
the on-disk `_run_journal/{sid}/` directory yet left the cached lock objects
behind, so a long-lived gateway leaked one entry per deleted run forever.

Delete now evicts every cached lock whose parent directory matches the removed
session (pid-independent) under `_WRITER_LOCKS_GUARD`, leaving unrelated
sessions' locks intact.

Refs #4633, #2097.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 21:13:32 +00:00
nesquena-hermes 6ab95bffa4 Merge pull request #5794 from nesquena/release/stage-5783
Release: serialize daemon-thread start under a lifecycle lock (#5783)
v0.51.927
2026-07-08 14:03:05 -07:00
nesquena-hermes 81a77efc91 docs(changelog): #5783 serialize daemon-thread start under lifecycle lock 2026-07-08 20:58:54 +00:00
ai-ag2026 d505b45545 fix(bg): serialize daemon-thread start under a lifecycle lock
start_drain_thread and start_session_channel_reaper checked is_alive()
and then created + started the daemon thread without holding a lock, a
check-then-act race: two concurrent callers could both see "not alive"
and each spawn a thread. The loser's thread was never stored in the
module global and ran forever, un-joinable by the matching stop_*. Both
check-then-start sequences now run under a dedicated
_THREAD_LIFECYCLE_LOCK (kept separate from the purpose-bound
SESSION_CHANNELS_LOCK / _EMIT_COALESCE_LOCK), so exactly one thread is
ever created.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 20:58:18 +00:00
nesquena-hermes 460f3eb47d Merge pull request #5793 from nesquena/release/stage-5781
Release: sweep completion-dedup map by delivery lifecycle (#5781)
v0.51.926
2026-07-08 13:47:35 -07:00
nesquena-hermes 4f9d63218b docs(changelog): #5781 sweep completion-dedup map by delivery lifecycle 2026-07-08 20:42:35 +00:00
ai-ag2026 15470a9a35 fix(bg): sweep completion-dedup map by delivery lifecycle, not channel (#4633)
BG_TASK_COMPLETE_EVENTS_SEEN gained a session_id -> set[process_id] entry the
first time a bg task completed for a session and was never deleted anywhere, so
it grew unbounded for the server lifetime.

The entry is created in _process_one for EVERY completion, whether or not any
SSE channel/tab exists, so pruning it only when the SessionChannel is reaped
would miss the dominant headless case (task fires, tab closed or never opened —
no channel to collect). Instead the reaper now sweeps the map by DELIVERY: once
a completion is drained (its session_id removed from PENDING_BG_TASK_COMPLETIONS)
the short _move_to_finished dedup window is closed and the entry is swept, every
tick, regardless of any channel. The registry's per-process_id
_completion_consumed gate remains the primary idempotency backstop, so sweeping a
delivered session's set can never resurrect an already-delivered completion.

Session deletion also prunes the entry (new forget_bg_task_completion_dedup),
covering a session deleted while a completion is still pending (which the
delivery-gated sweep deliberately retains).

Refs #4633
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 20:41:59 +00:00