Commit graph

7643 commits

Author SHA1 Message Date
webtecnica
f35081683a fix tests for #6385 scroll snapshot: update #4970 assertion + add behavioral coverage
- Fix test_stream_done_runs_scroll_preserving_collapse_pass_after_disarm:
  assert _doneLiveScrollSnapshot is captured before arm, and the
  argument-bearing _renderMessagesWithScrollSnapshot({_prescrollSnapshot:_doneLiveScrollSnapshot})
  call is used after disarm (the no-arg literal no longer exists).
- Add test_prescroll_snapshot_bypasses_capture_no_option_fallback_still_captures:
  Node.js behavioral harness that supplies a sentinel _prescrollSnapshot,
  stubs _captureMessageScrollSnapshot with a counter, and proves:
    1) sentinel reaches _restoreMessageScrollSnapshotSameFrame without a capture
    2) no-option / empty-{} calls still capture normally (callers at
       static/ui.js:9565, 14416, 14424).
2026-07-21 19:55:00 -03:00
sheldon-im
b5b21c180c fix: refresh composer action on resize fast path 2026-07-22 07:11:33 +09:00
koshikai
005589bc82 fix(i18n): drop workspace_artifact_source_session from ja
Per tests/test_issue6067_artifact_paths.py, this key is intentionally
English-fallback-owned (same convention as profile_concept_*) and must
be absent from every non-English locale block. CI caught this
(test_issue6067_session_source_key_is_english_fallback_owned) — the key
now falls through to LOCALES.en[key] as intended, same as before.
2026-07-21 21:52:02 +00:00
koshikai
62ac52ff17 feat(i18n): refresh Japanese (ja) locale to parity with upstream master
master was ~1,070 commits behind nesquena/hermes-webui:master, and roughly
40 of those touched static/i18n.js: new features (workspace panel, session
sharing, extensions gallery, cron sentence-builder UI, gateway controls),
plus a community PR that bulk-translated the old TODO-placeholder set
across all locales. Rebasing the old ja branch directly conflicted because
the English source text itself had moved since those translations were
written.

Rebuilt the ja block against current upstream/master instead: reused the
existing translation wherever the paired English source is unchanged, and
wrote fresh translations for everything new, reworded, or still sitting in
English. ja is back to full key parity with en per this repo's own
tests/test_japanese_locale.py contract (key coverage, duplicate-key
parity, placeholder preservation, arrow-function parity).

Left brand/acronym terms (MCP, YOLO, JSON, HTML, URL, sk-..., Hermes
Agent) and profile_concept_* (explicitly English-fallback-owned per
tests/test_issue2147_profile_concept_help.py) untouched, matching this
repo's existing convention across all locales.
2026-07-21 21:52:01 +00:00
Frank Song
72d5c62202 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-22 05:32:35 +08:00
nesquena-hermes
4fd314d033
docs(changelog): stamp #6312 terminal-error Worklog settlement hardening (exp-v0.52.137) (#6411) 2026-07-21 21:31:35 +00:00
Frank Song
ae8fb0abb7 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-22 05:31:35 +08:00
nesquena-hermes
5f91f5d564
Merge pull request #6312 from franksong2702/lifecycle-terminal-error-gate
fix: harden terminal-error Worklog duration + settlement (#6309)

Co-authored-by: Frank Song <franksong2702@gmail.com>
2026-07-21 14:24:57 -07:00
nesquena-hermes
5ff16c59d4 fix(streaming): catch OverflowError in _terminal_turn_duration so a huge/invalid pending_started_at can't abort terminal-error settlement
Codex gate on #6312 found the extracted helper caught only (TypeError, ValueError)
while the inline code it replaced caught Exception; float(10**400) raises
OverflowError, escaping the helper and aborting terminal-error cleanup before the
error message is persisted / apperror is emitted. Widen _terminal_turn_duration's
float() guard to (TypeError, ValueError, OverflowError) — the exact exceptions
float() can raise. Adds 10**400 to the existing invalid-origin parametrize
(red-before-green verified).
2026-07-21 20:06:17 +00:00
b3nw
34cc5dbeb2 fix: resolve transparent footer metadata from final segment (#6068 gate r2)
A tool-using Transparent Stream turn renders multiple assistant segments
(activity segment(s) then the final answer). Turn metadata — including
_usedModel — is stamped only on the final assistant message, but the
transparent footer read it via turn.querySelector('.assistant-segment'),
which returns the FIRST segment and thus no metadata. Combined with the
generic footer yielding model ownership to transparent mode, the model
label disappeared entirely on exactly the tool turns where per-turn
attribution matters most.

- Add _transparentTurnMetaMessage(turn): scan .assistant-segment
  [data-msg-idx] last→first for the final metadata-bearing assistant
  message, falling back to the last assistant segment.
- Use it for the transparent footer's duration/model/TTFT/usage instead
  of querySelector's first match.
- Non-blocking UX (per gate): preserve the full uncompacted model id in a
  hover title on both the transparent .lf-model span and the settled
  .msg-used-model-inline chip.
- Tests: behavioral multi-segment harness asserting exactly one .lf-model
  (and the correct final-segment metadata + hover title), plus wiring
  checks for the segment-scan helper.
2026-07-21 16:02:50 +00:00
b3nw
9bf876598d fix: stamp post-run agent.model + one model label per turn (#6068 gate)
Addresses the three gate-review blockers on #6113:

- Fallback attribution: read the served model from agent.model AFTER the
  run (the agent mutates it when a fallback fires) instead of the pre-run
  resolved_model, for both the persisted _usedModel and SSE used_model.
- Gateway double-label: suppress the additive chip whenever
  _gatewayRouting is present, and pass msg._usedModel to
  _formatGatewayModelLabel as its model fallback when routing omits
  used_model.
- Transparent double-label: skip the generic .msg-used-model-inline chip
  when the transparent turn footer owns the model label (.lf-model).

Also per Greptile: settled footer now orders the model chip after the
duration (elapsed - model - ...) to match the transparent footer, and the
test file gains its trailing newline.
2026-07-21 16:02:50 +00:00
b3nw
1598940091 test: behavioral _usedModelTurnChipLabel cases for #6068
Address gate review on PR #6113: eval the real footer helper in Node and
assert present, gateway-suppressed, and absent cases instead of ui.js greps.
2026-07-21 16:02:49 +00:00
b3nw
2e8d213e99 test: stub _usedModelTurnChipLabel in renderMessages Node harness
Fixes CI shard 2 after #6068 wired renderMessages to the new footer helper.
2026-07-21 16:02:49 +00:00
b3nw
30aef85b18 feat: stamp per-turn used model in footer stats (#6068)
Record resolved_model on assistant messages as _usedModel, expose used_model
in the usage SSE payload, and render model chips in transparent and settled
turn footers while suppressing duplicates when gateway routing already shows
used_model.
2026-07-21 16:02:49 +00:00
Frank Song
9bde2cf3af fix: reject future terminal turn origins 2026-07-21 15:05:17 +08:00
Frank Song
de96c1d384 fix: harden terminal error worklog settlement 2026-07-21 15:05:17 +08:00
Frank Song
dbbd6c111b Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 15:04:29 +08:00
nesquena-hermes
1dd5cc43ce
fix(tests): isolate model-selection env vars so the suite passes on any runner (#6395)
The suite failed 6 tests (test_sprint12 default-model, test_issue1538 Nous
catalog, test_issue1567 picker capacity/symmetry) whenever it ran on a box that
exports HERMES_MODEL (e.g. inside a live Hermes agent session where HERMES_MODEL
is the agent's runtime model). Root cause: get_effective_default_model() in
api/config.py treats HERMES_MODEL / OPENAI_MODEL / LLM_MODEL as the
highest-priority default-model source — above the isolated test config — so a
runner-exported value overrode the sandbox config and polluted the model
picker/catalog (e.g. injecting "opus-4.8" and breaking the @nous: prefix
invariant / default-model readback).

Fix: scrub the three model-selection env vars at conftest module level (before
any product module imports, covering in-process tests) and again in the
out-of-process test_server subprocess env, and add them to the per-test
_scrub_provider_env helpers in the two Nous test modules. The isolated config /
HERMES_WEBUI_DEFAULT_MODEL is now authoritative regardless of the runner env.

Verified: with HERMES_MODEL=opus-4.8 set, the full suite goes from 6 failed to
0 failed (13550 passed); non-vacuity confirmed (disabling the scrub reproduces
the failures).

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-21 00:00:52 -07:00
webtecnica
25d39da00e fix(stream): preserve reader viewport anchor through live-to-settlement collapse
Issue #6385: when a streaming turn settles, the two-render sequence
(keep-open expanded worklog → collapsed worklog) could displace the
reader's viewport because the second render captured its scroll snapshot
from the intermediate expanded state, not from the original live DOM.

Root cause
----------
The STREAM_DONE handler in messages.js:

1. Arms keep-settled-worklog-open token → renderMessages({preserveScroll:true})
   → worklog rendered EXPANDED (height-stable swap preventing shrink jump)

2. Disarms token → _renderMessagesWithScrollSnapshot()
   → This function called _captureMessageScrollSnapshot() which captured
     the scroll anchor from the expanded-worklog DOM (step 1 output),
     then called renderMessages with the worklog COLLAPSED (keep-open gone),
     then tried to restore from the expanded-state snapshot.

   The snapshot's semantic anchor (row key, session idx, top offset) was
   captured from a DOM where the worklog was expanded. After the collapse
   render the worklog is no longer at that position — anchor keys don't
   match, the semantic restore fails, and the viewport jumps to a
   unrelated scrollTop.

Fix
---
- Capture the scroll snapshot from the LIVE DOM (before any settlement
  renders) and pass it as  to the second render.
- Modify _renderMessagesWithScrollSnapshot() to accept a pre-captured
  snapshot via options._prescrollSnapshot, using it instead of capturing
  a fresh one from the current DOM state.

This ensures the collapse render anchors to the content the reader was
actually viewing, not to a stale intermediate expanded-worklog position.

Co-authored-by: webtecnica <webtecnica@users.noreply.github.com>
2026-07-21 02:12:06 -03:00
Frank Song
9e7106de33 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 12:55:27 +08:00
nesquena-hermes
c0d9d81512
Release: add preference to hide new-chat welcome panel (#6183, @vaidu-ai) (#6387)
* feat: add option to hide new-chat welcome panel

* Release: add preference to hide new-chat welcome panel (#6183, @vaidu-ai)

---------

Co-authored-by: vaidu-ai <im@vaidu.net>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-21 04:45:11 +00:00
Frank Song
c1ce5a88a7 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 12:42:37 +08:00
nesquena-hermes
4f063d16f8
Release: Artifacts pane long-filename readability + long-parent containment (#6078, @rodboev) (#6386)
* fix(#6067): keep artifact file names visible

# Conflicts:
#	static/style.css
#	static/workspace.js

* fix(#6067): bound long parent artifact tails inside the drawer

* Release: Artifacts pane long-filename readability + long-parent containment (#6078, @rodboev)

---------

Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-21 04:39:25 +00:00
Frank Song
0ea013ca7e Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 12:05:10 +08:00
nesquena-hermes
6b9b9dc808
Release: Kanban New-Task modal reachable on mobile (#6301, @jpalazz2) (#6384)
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-21 03:57:21 +00:00
Frank Song
8b6241df54 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 11:49:33 +08:00
nesquena-hermes
48b40d930a
Release: isolate Hermes home per streaming turn (#5877, @starship-s) (#6379)
* fix(profiles): isolate Hermes home per streaming turn

Assisted-by: OpenCode:gpt-5.3-codex-spark
Assisted-by: Hermes Agent:gpt-5.6-sol
Assisted-by: Codex:gpt-5.3-codex-spark

* fix(profiles): gate skill isolation by capability

Assisted-by: OpenCode:gpt-5.3-codex-spark
Assisted-by: Hermes Agent:gpt-5.6-sol
Assisted-by: Codex:gpt-5.3-codex-spark

* fix(profiles): harden fallback lock boundaries

Assisted-by: OpenCode:gpt-5.3-codex-spark
Assisted-by: Hermes Agent:gpt-5.6-sol

* test(profiles): adapt streaming isolation harness

Assisted-by: Codex:gpt-5.3-codex-spark
Assisted-by: Hermes Agent:gpt-5.6-sol

* Release: isolate Hermes home per streaming turn (#5877, @starship-s)

---------

Co-authored-by: starship-s <45587122+starship-s@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-21 03:48:00 +00:00
Frank Song
fea77cc5ad Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 11:28:00 +08:00
nesquena-hermes
0deef392f7
Release: consolidated Kanban board fills vertical space (#6308) (#6376)
* fix: Fix height of consolidated kanban board

There was a lot of empty space below the kanban board columns
in the condolidated view (particularly on desktop). Modify the
CSS such that the consolidated view always fills the viewport
vertical space.

* Release: consolidated Kanban board fills vertical space (#6308)

---------

Co-authored-by: Joe Palazzolo <joe@joepalazzolo.net>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-21 03:26:57 +00:00
Frank Song
14180a92b9 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 10:57:58 +08:00
nesquena-hermes
a725a0f71c
Release p1-batch: session cache cap 300→100 (#6362) + virtualization comment (#6318) (#6375)
* perf(transcript): enable DOM virtualization by default (re-enable #4346 fix) (#6151) (#6155)

* fix: revert DOM virtualization default to opt-in, fix gate RED (#6155)

* fix(#6351): lower default session cache cap

* Release p1-batch: session cache cap 300->100 (#6362) + virtualization comment (#6318)

---------

Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>
Co-authored-by: webtecnica <webtecnica@users.noreply.github.com>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-21 02:53:43 +00:00
Frank Song
9dfe1367c7 Preserve historical anchor final text 2026-07-21 04:42:59 +08:00
Frank Song
7fa8f9415d Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 04:22:28 +08:00
nesquena-hermes
302843c89f
Release: compact completed image tool-results (#6315, @sjungwon03) (#6371)
* fix(session): compact completed native vision results

* streaming: guard image-part compaction against unhashable part.type (isinstance str) + regression test [maintainer fix on @sjungwon03 #6315]

* docs(changelog): stamp #6315 completed-image tool-result compaction

---------

Co-authored-by: sjungwon03 <sjungwon03@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 20:11:29 +00:00
Frank Song
c9a96794ce Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 01:56:35 +08:00
nesquena-hermes
c414c39f10
feat(extensions): token-v1 proxy→sidecar authentication boundary (#6331)
* feat(extensions): token-v1 proxy->sidecar auth boundary

Mint a per-extension secret core injects (X-Hermes-Sidecar-Token) on every
proxied request; sidecars validate it. Closes the hole where a loopback sidecar
port is reachable by any local process and cannot distinguish a proxied request
from a direct one.

- api/extension_sidecar_auth.py: per-extension token lifecycle (atomic mint,
  re-read-verified so an unpersisted token is never injected, per-request
  mtime-cached read for live rotation, path-escape-safe)
- manifest proxy_auth negotiation: absent=legacy, token-v1=enforce, unknown=fail-closed
- proxy: inject token, strip inbound + response x-hermes-*, auth-off posture
  (loopback-only local_unprotected, else 503), fail-closed when token unavailable
- consent-time auth_required in status payload; mint-on-consent
- docs/EXTENSIONS.md proxy_auth section
- 7 new tests (26/26 green)

* fix(extensions): address Codex+Fable gate on token-v1 (6 findings)

- align token-module extension-id grammar with core _EXTENSION_ID_RE (was
  narrower -> legally-named ext consented then 503'd forever)
- resolve token dir dynamically (mirror _extension_state_dir) -> real test
  isolation, no import-time STATE_DIR cache
- cross-process mint: O_CREAT|O_EXCL no-clobber claim (was os.replace clobber)
- rotation cache keyed on full fingerprint (ino/dev/mtime/ctime/size) +
  re-fingerprint after read -> same-size/mtime replacement no longer stale
- validate token format on read (url-safe, 16-256) -> malformed file can't
  leak via a ValueError echoed in a 502
- consent fails 503 when token can't be provisioned (was silent-swallow ->
  persisted consent then 503 forever)
- rename status auth_required -> posture enum (protected|local_unprotected):
  nothing is blocked for loopback, so 'required' was misleading
- panels.js: render local_unprotected warning on the consent row (+ CSS)
- docs: token-path resolution order, 401-vs-503, explicit 'legacy' acceptance
- tests: route-level token-injection+response-strip test; fix illusory isolation
  in token-module test; 27/27 green

* fix(extensions): close 2 token-mint races (Codex re-gate round 2)

- mint via temp-file + atomic os.replace (not O_CREAT|O_EXCL) so the final
  path is never observed empty/half-written — a concurrent loser can no longer
  read an empty token file and 503
- single _stable_read helper (fingerprint-read-refingerprint, bounded retry on
  mid-read change) used by BOTH ensure_token and current_token — a token that
  changes during the read is never returned or cached stale
- stress-verified: 20 concurrent first-mints converge on 1 persisted token; 27/27

* fix(extensions): atomic no-clobber token publish via os.link (Codex re-gate round 3)

os.replace fixed empty-file exposure but still clobbered cross-process: two
processes could both write+replace and a reader between them got a token no
longer on disk -> 401. Switch to the repo's TOCTOU-safe os.link create-or-fail
idiom (session_recovery.py:627): write temp -> link into place (fails if a
winner already published) -> loser drops its temp and reads the winner via
_stable_read. Proven: 16 concurrent PROCESSES converge on 1 persisted token,
all matching disk. 27/27 tests green.

* fix(extensions): resolve Frank+Greptile #6331 review — token-v1 fail-closed when auth off (consent+resolution), token-v1-only proxy_auth/posture status fields, fullmatch ext-id validator

* fix(extensions): finish Frank and Greptile sidecar review

---------

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 17:49:23 +00:00
Frank Song
d3374c50d0 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-21 00:40:44 +08:00
nesquena-hermes
33d55ec897
Release: raster-data-URI redaction fast-path (#6311, @inch772) (#6367)
* fix(redaction): skip native raster data URIs

* fix(redaction): accept mixed-case raster MIME types

* fix(redaction): validate complete raster payloads

* docs(changelog): stamp #6311 raster-data-URI redaction fast-path

---------

Co-authored-by: Su Ahn Lee <11433303+inch772@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 16:30:04 +00:00
Frank Song
39ef186a20 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 22:44:22 +08:00
nesquena-hermes
4bdc22b7ce
Release: gateway-default MoA send (#5869, @rodboev) (#6365)
* fix(#5853): allow gateway-default MoA sends

* fix(#5853): freeze gateway auth to one locked snapshot

* docs(changelog): stamp #5869 gateway-default MoA send

---------

Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 14:29:22 +00:00
Frank Song
2e9c2b1dc8 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 19:03:56 +08:00
nesquena-hermes
a61880b977
Release: terminal-error lifecycle gate row (#6354, @franksong2702) (#6358)
* test: add terminal-error lifecycle matrix row

* test: reject empty terminal process rows

---------

Co-authored-by: Frank Song <franksong2702@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 10:52:18 +00:00
Frank Song
c2b5a54b38 Harden historical anchor hydration edges 2026-07-20 17:22:02 +08:00
sheldon-im
8e55540da2 test: cover composer resize boundaries 2026-07-20 18:15:29 +09:00
Frank Song
a91f34a5f0 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 17:14:28 +08:00
nesquena-hermes
be29c3f7c0
Release: run-journal summary cache (#6291, @sjungwon03) (#6355)
* perf(run-journal): cache unchanged run summaries

* fix(run-journal): reject cache after missing-file race

* run-journal cache: add st_ctime_ns to signature (close same-size mtime-preserving rewrite window) + regression test [maintainer fix on @sjungwon03 #6291]

* docs(changelog): stamp #6291 run-journal summary cache

---------

Co-authored-by: sjungwon03 <sjungwon03@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 09:12:32 +00:00
Frank Song
ed5301343d Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 13:05:05 +08:00
nesquena-hermes
dd554c1788
Release: Live Stream Anchor side-effects projection (#6204, @franksong2702) (#6348)
* fix: preserve anchor-owned side effects

* test: prove invisible anchor outcomes do not repaint

* docs(changelog): stamp #6204 Anchor side-effects projection

---------

Co-authored-by: Frank Song <franksong2702@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 04:56:51 +00:00
sheldon-im
6a30d74ac7 a11y: avoid no-op composer height resets 2026-07-20 13:43:09 +09:00
Frank Song
3fa08e4a22 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 12:17:39 +08:00