Commit graph

5270 commits

Author SHA1 Message Date
nesquena-hermes
30dfd40320 test(#4932): re-anchor #4927 derived-rebuild slice to fallbackToolSources
#4932 added a second 'const resultsByTid={}' (in _collectToolResultSnippetsByTid)
EARLIER in ui.js than the renderMessages derived rebuild, so the #4927 test's
UI_JS.index('const resultsByTid={};') sliced the wrong region and 4 assertions
failed. Re-anchor on 'const fallbackToolSources=[];' which is unique to the
derived-rebuild block. Pure test fix, no behavior change.
2026-06-25 21:40:25 +00:00
nesquena-hermes
c4b9edc84e fix(#4932): wire ordered transparent path to #4927 durable snippet fallback + gate scans to transparent mode
Codex+Opus gate (both reproduced): the new mixed-content ordered render path
resolved tool snippets only from the S.messages join (_collectToolResultSnippetsByTid)
and never consulted persistedSnippetByTid (S.session.tool_calls) — so on a
cold/paginated load where the tool_result join misses (#4927 scenario) the inline
card rendered an EMPTY body, AND its tid was added to transparentOrderedToolIds,
suppressing the post-loop derived card that WOULD have recovered via #4927. Build
a transparentPersistedSnippetByTid map from S.session.tool_calls and thread it as a
5th arg to _transparentOrderedToolCall, falling back in both the live and non-live
branches. Also gate the transparent-only scans (S.toolCalls + _collectToolResultSnippetsByTid)
behind isTransparentStream() so non-transparent renders skip the O(messages×parts)
work (Opus perf finding). Redaction (#4926/#4928) was already safe (shared buildToolCard).
+ cold-load persisted-fallback regression test.
2026-06-25 21:21:28 +00:00
Rod Boev
3a406512b0 Retrigger stalled CI for transparent-stream follow-up 2026-06-25 21:11:56 +00:00
Rod Boev
5f1a2a07de Keep settled transparent turns aligned with anchor-scene ownership 2026-06-25 21:11:56 +00:00
Rod Boev
bef89969ed fix(chat): preserve recycle harness anchors after prose split (#3397) 2026-06-25 21:11:56 +00:00
Rod Boev
b590518cf4 fix(chat): preserve transparent stream prose order for mixed turns (#3397) 2026-06-25 21:11:55 +00:00
nesquena-hermes
ada386acd4
Release XS (v0.51.663): show mobile reload button outside standalone mode (#4939, fixes #4784)
Release XS (v0.51.663): show mobile reload button outside standalone mode (#4939, fixes #4784)
2026-06-25 13:55:08 -07:00
nesquena-hermes
3bc712af97 Release XS (v0.51.663): show mobile reload button outside standalone mode (#4939, fixes #4784) 2026-06-25 20:51:39 +00:00
Rod Boev
779d46a1cf fix(#4784): show the reload button outside standalone mode 2026-06-25 20:41:46 +00:00
nesquena-hermes
a569d6ba3b
Release XR (v0.51.662): ignore DOM-wipe scroll clamp during render (#4934)
Release XR (v0.51.662): ignore DOM-wipe scroll clamp during render (#4934)
2026-06-25 13:38:51 -07:00
nesquena-hermes
2c1b0f00fd Release XR (v0.51.662): ignore DOM-wipe scroll clamp during render (#4934) 2026-06-25 20:35:20 +00:00
allenliang2022
ef6901c947 fix(chat): ignore DOM-wipe scroll clamp during render 2026-06-25 20:19:00 +00:00
nesquena-hermes
a06a63a9c2
Release XQ (v0.51.661): no fabricated turn duration from stale pending_started_at (#4930)
Release XQ (v0.51.661): no fabricated turn duration from stale pending_started_at (#4930)
2026-06-25 13:14:30 -07:00
nesquena-hermes
2e387d0ed1 Release XQ (v0.51.661): no fabricated turn duration from stale pending_started_at (#4930) 2026-06-25 20:11:01 +00:00
nesquena-hermes
12d1cec90a fix(#4930): don't render a fabricated turn duration from a stale pending_started_at
A settled turn that recorded no live duration fell back to
(Date.now()/1000) - session.pending_started_at. But pending_started_at is the
start of an IN-FLIGHT turn, so for a settled turn it is either stale (a session
that sat idle) or about a different pending turn — rendering a bogus
'Processed 15h 32m' on fresh conversations (#4930). Gate the fallback on a turn
actually being in flight (active_stream_id || pending_user_message); otherwise
return undefined (no duration shown) rather than a fabricated one. The recorded
live duration (_turnDuration / base.turn_duration) still takes priority, so this
only removes bogus durations, never a correct one.
2026-06-25 19:53:19 +00:00
nesquena-hermes
9d1b6617c2
Release XP (v0.51.660): durable tool output/diff body on cold reload (#4927, completes #4925)
Release XP (v0.51.660): durable tool output/diff body on cold reload (#4927, completes #4925)
2026-06-25 12:44:48 -07:00
nesquena-hermes
c9c3d8b8e9 Release XP (v0.51.660): durable tool output/diff body on cold reload (#4927, completes #4925) 2026-06-25 19:41:51 +00:00
nesquena-hermes
4030dca989 fix(#4927): persist loaded session.tool_calls so the cold-load fallback has a source
Codex gate (the key one): the persistedSnippetByTid fallback read S.session.tool_calls,
but loadSession() keeps the messages=0 session object (tool_calls []), and the
real messages=1 summary arrives only as the sessionToolCalls argument to
_syncToolCallsForLoadedMessages() — never copied onto S.session. So the fallback
source was empty on exactly the cold-load path it repairs. Persist
sessionToolCalls onto S.session.tool_calls in _syncToolCallsForLoadedMessages
(covering _ensureMessagesLoaded, older-message paging, and full-history loaders)
so renderMessages builds a populated persistedSnippetByTid. + guard test.
2026-06-25 19:29:06 +00:00
nesquena-hermes
0caa57d8b7 fix(#4927): cover the Anthropic tool_use derived push with the persisted fallback
Codex gate: the third derived-push site (Anthropic tool_use content-array branch,
ui.js) still read resultsByTid[tid]||'' without the persisted fallback, so
Anthropic-format sessions could still cold-reload with an empty tool body. Add
the persistedSnippetByTid fallback there too; now all derived result-snippet
reads (OpenAI top-level, Anthropic tool_use, partial) chain to the durable
persisted snippet. Added a guard test asserting EVERY derived resultSnippet read
includes the fallback.
2026-06-25 19:23:31 +00:00
nesquena-hermes
dad0a3554d fix(#4927): durable fallback for tool output/diff body on cold reload
In the transparent stream, terminal output and patch diffs intermittently
vanished after a cold/paginated reload. A settled tool row renders its body from
`snippet`, but the raw-assistant-envelope 'derived' rebuild only joined the
result by tool id via resultsByTid (role:tool messages); when that join missed
(id mismatch, recovery-rebuilt turn) the snippet was empty, and #4622's live
enrichment only helps when S.toolCalls still matches (empty on cold load).

Build a persistedSnippetByTid lookup from the durable session.tool_calls compact
summary (which carries a bounded result snippet per tid) and use it as a
fallback in all three derived-push sites, so the persisted record is the
reliable source and the live join becomes a fast-path, not the only path that
shows the body. Part of #4925.
2026-06-25 19:14:23 +00:00
nesquena-hermes
ccab526528
Release XO (v0.51.659): one-click extension install works with no env config (#4935, closes #4933)
Release XO (v0.51.659): one-click extension install works with no env config (#4935, closes #4933)
2026-06-25 11:59:25 -07:00
nesquena-hermes
3e5446ff8a Release XO (v0.51.659): one-click extension install works with no env config (#4935, closes #4933) 2026-06-25 18:55:33 +00:00
nesquena-hermes
0562bdbd31 fix(#4933): one-click extension install works with no env (bootstrap managed default root)
Settings -> Extensions offered an Install button even when extensions were
unconfigured, then dead-ended on 'Extensions not configured' because
install_extension required HERMES_WEBUI_EXTENSION_DIR to be set to an existing
directory. Default to a WebUI-managed root under STATE_DIR/extensions and
bootstrap it on first install so install just works with zero config; the env
var remains an optional override (never auto-created). Gallery-installed
extensions already auto-load via the runtime manifest, so the extension loads
on the next app-shell render with no further setup.

- api/extensions.py: _default_extension_root + _writable_extension_root;
  _extension_root/_extension_root_status fall back to the managed default;
  install_extension bootstraps via _writable_extension_root; dir-unavailable
  warning only fires for an explicitly-set-but-broken env path.
- tests: update disabled-by-default expectations (configured=True, valid=False
  pre-install) + new regression proving install bootstraps + auto-loads with
  no env.
- docs/EXTENSIONS.md: document zero-config one-click install + env now optional.
- CHANGELOG [Unreleased].
2026-06-25 18:45:09 +00:00
nesquena-hermes
670fe37f4c
Release XN (v0.51.658): tool cards keep long content args + reconstruct diffs (#4928, #4925)
Release XN (v0.51.658): tool cards keep long content args + reconstruct diffs (#4928, #4925)
2026-06-25 11:42:30 -07:00
nesquena-hermes
7d5d98a005 Release XN (v0.51.658): tool cards keep long content args + reconstruct diffs (#4928, #4925) 2026-06-25 18:39:00 +00:00
nesquena-hermes
f79c975842 fix(#4928): redact tool args at all render/copy sites now that content args are full-length
Codex gate: raising content args from 120->4000 chars newly surfaces secrets on
non-first lines / past char 120 in render paths that print tc.args directly and
bypass the #4926 label redaction. Apply _redactToolTargetLabel to arg values at
every such site: the transparent Full-tab args render (_transparentToolDetailHtml),
the non-transparent tool-card args block (buildToolCard visibleArgs), the
clipboard copy path (_copyEventToClipboard), and the _toolArgsSnapshot itself.
+ node regression proving a secret past char 120 in a long command is masked in
the Full tab while the non-secret command structure is retained.
2026-06-25 18:28:06 +00:00
nesquena-hermes
bd6e57fb42 fix(#4928): don't cap tool-call content args to 120 chars
Tool-call args were truncated to 120 chars at three sites (_truncate_tool_args,
the live _tool_args_snapshot in api/streaming.py, and _toolArgsSnapshot in
static/ui.js). For incidental args that's fine, but content/diff-bearing keys
(command/cmd/script/code/patch/diff/old_string/new_string/content/path/
file_path) feed the rendered tool card AND the diff reconstruction in
recovery-rebuilt sessions, so the cap silently corrupted long commands/paths and
produced broken/empty diffs. Exempt those keys with a much larger cap
(_TOOL_ARG_CONTENT_CAP = _TOOL_RESULT_SNIPPET_MAX = 4000) at every site, keyed by
key name; incidental args keep the 120 cap; storage stays bounded. Part of #4925.
2026-06-25 18:18:17 +00:00
nesquena-hermes
55a2e698a9
Release XM (v0.51.657): expanded shell tool card full command + broadened redaction (#4926, #4925)
Release XM (v0.51.657): expanded shell tool card shows full command + broadened redaction (#4926, #4925)
2026-06-25 11:11:36 -07:00
nesquena-hermes
a5bf6b9f76 Release XM (v0.51.657): expanded shell tool card shows full command + broadened redaction (#4926, #4925) 2026-06-25 18:08:07 +00:00
nesquena-hermes
64cb4cef4e fix(#4926): env-assignment redaction must not eat URL query tails
Codex re-gate: the env/AUTH '=' redaction regex matched 'token=' INSIDE a URL
(?token=...&mode=debug&signature=...) before the dedicated URL-query redactor,
and its \S+ value swallowed the rest of the URL incl later params + closing
quote. Anchor the env-assignment match to a shell-token boundary (^|[\s;|(]) so
it can't match keys preceded by ? or & — URL query params are left to the
dedicated query-param redactor, which masks the secret-looking ones while
preserving the command tail. + regression tests (URL-tail-preserved,
benign-PATH-not-over-redacted).
2026-06-25 17:58:12 +00:00
nesquena-hermes
ed21bd20cd fix(#4926): broaden _redactToolTargetLabel to mask env/flag/header secrets in the full command
Codex gate: exposing the full multi-line command in the expanded shell card
newly surfaces secrets on non-first lines (the collapsed header only ever showed
line 1). The old redactor masked only password-shaped fields. Broaden it to mask
common secret forms across the WHOLE command: env/colon assignments for
*TOKEN/*API_KEY/*SECRET/*ACCESS_KEY/*PRIVATE_KEY/*CREDENTIAL/*CLIENT_SECRET/
*SESSION_KEY and *AUTH*=, --token/--api-key/--secret style flags,
Authorization: Bearer/Bot/Token headers, and secret-looking URL query params.
Test now drives the REAL redactor (not an identity stub) and asserts non-first-line
secrets are masked while the collapsed header stays first-line.
2026-06-25 17:52:41 +00:00
nesquena-hermes
99b6a5e30b fix(#4926): expanded shell tool card shows the full multi-line command
In the transparent activity stream, a shell tool card's collapsed header
correctly shows only the first line of the command, but the EXPANDED detail
lead (`$ <command>`) reused that same first-line-only value via
_toolTargetLabel, so a multi-line script still rendered as line 1 when expanded.
The full command is available frontend-side; the truncation was purely in the
label derivation.

Adds _toolFullCommandLabel(tc) (full multi-line, redacted, no first-line
collapse) and uses it in _toolDetailLeadText for the shell kind, while the
collapsed header keeps the first-line _toolTargetLabel. Part of #4925.
2026-06-25 17:39:05 +00:00
nesquena-hermes
773a36dd91
Release XL (v0.51.656): stream writeback timing diagnostics (#4923, #4918)
Release XL (v0.51.656): stream writeback timing diagnostics (#4923, #4918)
2026-06-25 09:58:27 -07:00
nesquena-hermes
7d5a7ebd2f Release XL (v0.51.656): stream writeback timing diagnostics (#4923, #4918) 2026-06-25 16:54:53 +00:00
nesquena-hermes
f377215a0d harden: wrap _log_stream_writeback_timings in try/except so a misbehaving log handler can't skip the bg-title thread (Opus gate nicety, #4923) 2026-06-25 16:54:20 +00:00
Frank Song
8b1f8fe5ca Add final stream writeback diagnostics 2026-06-25 16:42:53 +00:00
nesquena-hermes
c33657434f
Release XK (v0.51.655): avoid repeated session loads in live usage metering (#4922, #4918)
Release XK (v0.51.655): avoid repeated session loads in live usage metering (#4922, #4918)
2026-06-25 09:38:20 -07:00
nesquena-hermes
f0448a9cee Release XK (v0.51.655): avoid repeated session loads in live usage metering (#4922, #4918) 2026-06-25 16:26:05 +00:00
Frank Song
7d69b80012 Avoid repeated session loads in live usage metering 2026-06-25 16:12:18 +00:00
nesquena-hermes
1fcb61cc04
Release XJ (v0.51.654): narrow session-index save LOCK window (#4921, #4918)
Release XJ (v0.51.654): narrow session-index save LOCK window (#4921, #4918)
2026-06-25 09:07:47 -07:00
nesquena-hermes
0e5de32c20 Release XJ (v0.51.654): narrow session-index save LOCK window (#4921, #4918) 2026-06-25 16:04:11 +00:00
nesquena-hermes
3ca84b61d3 docs: clarify _INDEX_WRITE_LOCK serializes the on-disk index RMW (Opus gate nicety) 2026-06-25 16:03:36 +00:00
Frank Song
3fd88903b0 Remove contributor changelog entry 2026-06-25 22:55:27 +08:00
Frank Song
3bae596ae8 Reduce session index global lock window 2026-06-25 22:02:34 +08:00
nesquena-hermes
cdb70ee547
Release XI (v0.51.653): keep gateway approval failures actionable (#4917, closes #4771)
Release XI (v0.51.653): keep gateway approval failures actionable (#4917, closes #4771)
2026-06-25 06:35:12 -07:00
nesquena-hermes
c0d65c8c2f Release XI (v0.51.653): keep gateway approval failures actionable (#4917, closes #4771) 2026-06-25 13:32:15 +00:00
Rod Boev
e67870caa5 test(#4771): align approval CI assertions with helper flow 2026-06-25 13:09:42 +00:00
Rod Boev
7d617d91f7 fix(#4771): block duplicate approval submits 2026-06-25 13:09:42 +00:00
Rod Boev
0b12b3b1fb test(#4771): cover both approval rejection paths 2026-06-25 13:09:42 +00:00
Rod Boev
528fa72390 fix(#4771): keep gateway approval failures actionable 2026-06-25 13:09:42 +00:00