Commit Graph
487 Commits
Author SHA1 Message Date
nesquena-hermes 30fb4edc1f fix(i18n): stop double-rendering the skip-all lightning icon (#7701)
The approval card's Skip-all button drew its icon twice: once from the
.approval-btn-icon span in static/index.html and again from the leading
glyph baked into the approval_skip_all translation in 14 of 15 locales.
pt already shipped without the prefix, which is the correct shape.

Strip the glyph from every locale value so the markup span is the single
icon source, matching Allow once / Allow session / Always allow / Deny,
which all pair an icon element with a glyph-free label.

test_vietnamese_locale.py asserted the old prefixed string verbatim; its
expectation is updated to the corrected value.
2026-09-22 06:17:58 +00:00
Yularzhi 4d2b35b943 fix(i18n): route Extensions panel through t() and patch 15 locales (#7581)
The Extensions settings pane (Gallery / Installed / Diagnostics) was the
last large user-facing surface still rendering hardcoded English:

- static/index.html carried the section meta, both loading placeholders,
  the Copy diagnostics button, and the whole Trust-model explainer block
  as plain HTML with no data-i18n attributes.
- static/panels.js built the runtime / diagnostics cards from template
  literals with 14 untranslated row labels, a separate installed card,
  asset and warning card titles, sidecar card fields (Origin / Health
  path / Health URL / Proxy / Proxy path), the Enabled/Disabled and
  true/false badges, and every empty-state string (None, No warnings,
  No extension directory is configured., No manifest extensions are
  installed in the configured bundle., No loopback sidecars declared.)
  inline in JS.

Every one of those call sites now routes through t() and the plain
strings live in the locale bundles. data-i18n is used for the static
HTML (the same mechanism the other selects already use, per #7582
triage), and esc(t(...)) for the template-literal DOM built in
panels.js so no injected text can introduce markup.

Locale keys are added to all 15 shipped locale bundles (en, ru, it, ja,
es, de, zh, zh-Hant, pt, ko, fr, cs, tr, pl, vi), following the same
key-parity contract that the #7602 triage enforced for the
sessions_source keys: inserted right after each locale's identity
(_label:_speech_) line so the brace-bounded locale extraction still
finds the whole block, matching the same pattern upstream used to fix
the sessions_source parity. No locale falls back to English at runtime
because t() resolves every bundle.

tests/test_extensions_settings_panel.py: assertions that used to match
the inline English strings now match the corresponding locale keys, and
the state_unknown_ids hint asserts both the key is used and the raw
English is gone.

Verified:
- python3 -m pytest tests/ -k "i18n or extension or parity or japanese"
 -q  ->  496 passed
- node --check on the three touched static assets -> all valid

Fixes #7581. Same surgical slice as #7602 (linked from #7579).
2026-09-17 09:20:41 +03:00
2ed0c4db27 fix(i18n): route sidebar source-tab labels through t() (#7580) (#7602)
* fix(i18n): route sidebar source labels through t() (#7580)

_sessionSourceLabel() hardcoded the two visible tab labels as English
template literals and assigned the result straight to textContent, so
non-English locales rendered untranslated sidebar tabs. Route both
filters through t() with the established {0}-interpolation pattern
(same shape as kanban_comments_count) and add explicit en + ru keys
so English remains the fallback for locales without a translation.

- static/sessions.js: return t("sessions_source_cli", n) /
  t("sessions_source_webui", n) instead of template literals
- static/i18n.js: add the two keys to the en and ru bundles
- tests/test_issue2351_cli_session_source_filter.py: replace the
  hardcoded-English regressions with locale-key assertions and a new
  interpolation test; verify the template-literal fallback is gone
  so a regression can no longer ro-introduce it silently

* i18n(sessions-source): add source-tab labels to the remaining 13 locales

Codex gate: the runtime fallback is non-regressive, but the repo enforces a
locale key-parity contract — 9 parity tests failed because the two new keys
existed only in EN+RU. Add sessions_source_webui / sessions_source_cli to it,
ja, es, de, zh, zh-Hant, pt, ko, fr, cs, tr, pl, vi, matching each locale's
established translation of 'session' and preserving the {0} count placeholder.
Placed after each locale's _label line so the {0} literal does not fall inside
test_japanese_locale.py's brace-bounded _label regex.

Co-authored-by: Yularzhi <Yularzhi@users.noreply.github.com>

---------

Co-authored-by: nesquena-hermes <agent@hermes.local>
Co-authored-by: Yularzhi <Yularzhi@users.noreply.github.com>
2026-09-16 02:39:58 -07:00
asakuraandnesquena-hermes 155838f2c3 fix(i18n): unify ja locale profile wording (プロフィール → プロファイル) (#6762)
Co-authored-by: nesquena-hermes <nesquena+hermes@gmail.com>
2026-08-14 16:43:19 +00:00
webtecnicaandnesquena-hermes 3d18a8ca5a i18n: translate workspace-sort keys in 14 non-English locales (#6902) (#6910)
Co-authored-by: nesquena-hermes <nesquena+hermes@gmail.com>
2026-08-13 22:39:25 -07:00
nesquena-hermesandrodboev 8b873c55eb Release exp-v0.52.198: Refine selected transcript text in composer (#6410, @rodboev)
Ships PR #6410 with a maintainer UX fix folded in (authorship preserved).

Adds a 'Refine' action to the message text-selection toolbar: select text in a
chat message -> Refine seeds it into the composer as an editable quoted draft
(marker-free, one-way, existing draft + attachments preserved, 44x44 mobile
icon buttons, all 15 locales). ChatGPT/Notion-style quote-to-edit flow.

Maintainer fix (Fable UX gate): French instruction string now uses the
space-before-colon convention (Instruction d'affinage :).

Gate: Codex SAFE TO SHIP (marker-free, no XSS, one-way Range, Reply/drafts
preserved), Fable SHIP-WITH-UX-FIXES (fix applied), tests 19/19, ESLint clean.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-08-11 02:19:06 +00:00
nesquena-hermes f50a80beb6 Merge branch 'pr6055stage' into release/stage-6055b-1786393156 2026-08-10 20:19:18 +00:00
nesquena-hermes 6f7fa623cb Merge branch 'pr-6091-head' into release/stage-6091-1786388870 2026-08-10 19:07:52 +00:00
Vincent Dukeandnesquena-hermes 64c2c12df7 feat(sidebar): add show_kanban_sessions toggle to hide kanban worker sessions (#6780)
* feat(sidebar): add show_kanban_sessions toggle to hide kanban worker sessions

* fix(sidebar): kanban source-filter override + filtered-view cap (cron/webhook parity)

Gate re-review (Codex) found the kanban-hide addition missed cron/webhook
parity for the source-filter case:
- An explicit kanban source_filter now overrides the default hide (threaded
  source_filter into _dedupe_cli_sidebar_sessions_for_api, normalized with
  strip().lower() to match the loader), so a deliberate kanban view shows
  those rows even with the toggle off. Scoped to the matching source only.
- A dedicated KANBAN_PROJECT_CHIP_LIMIT bounds a filtered kanban-only view,
  mirroring CRON/WEBHOOK_PROJECT_CHIP_LIMIT.

Deliberately NOT changing the general exclude_sources: the original PR lets
kanban load on the source_filter=None path so the toggle-ON case works (the
hide is applied by _hide_from_default_sidebar only when the toggle is off).
Excluding kanban there would save the 20-row budget but break toggle-on
unless a bounded kanban merge is added (cron/webhook don't do that either) —
that query-budget optimization is deferred as a separate cross-cutting change.
The pinned-survives-hide finding is also out of scope (cron/webhook hide pinned
identically, confirmed by Codex). Adds bite-tested regressions.

Co-authored-by: vduke <vduke@users.noreply.github.com>

---------

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-08-09 02:53:25 +00:00
Rod Boev eb9298ba91 fix(#5932): omit unchanged board workdir on settings edits 2026-08-08 15:44:55 -04:00
Rod Boev 2b0614ce35 feat(#5932): add per-board default workdir 2026-08-08 15:44:55 -04:00
pxxD1998 e2b052a637 fix(workspace): persist recovered session bindings 2026-07-29 18:22:22 +00:00
b3nwandb3nw e93b0c352a feat: render process wakeups as collapsed summary cards (#6350)
Render structured background-process completions and watch matches as accessible collapsed cards while preserving raw fallback, verbatim output, and full watch patterns.

Co-authored-by: b3nw <b3nw@duck.com>
2026-07-23 20:52:51 +00:00
nesquena-hermesandrodboev 575e45f76b polish(#6040): approval-counter CSS cleanup + i18n the pending-count string
Fable UX polish pass on the gateway approval counter:
- Move the crude inline style (font-size:0.75em;opacity:0.6;margin-top:4px) into
  a proper .approval-counter rule: 11px, color:var(--muted) (skin-safe vs opacity),
  margin:0 0 12px (fixes the 0px gap that left it flush against the buttons),
  tabular-nums for stable digits.
- i18n the '1 of N pending' string (was the only hardcoded-English string in an
  otherwise fully-localized approval card): new approval_pending_count key in en
  + 14 locales, wired via t() with an English fallback.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-23 03:12:49 +00:00
Rod Boev f30178e18a fix(gateway): fall back safely without approval identities 2026-07-22 12:31:12 -04: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
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
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
Rod Boev 5771a511e7 fix(#6099): make transparent stream activity timestamps optional (#6130)
Add an opt-in setting to hide Transparent Stream's per-event timestamp chips while keeping the response footer time visible, for users who found the per-event chips noisy. Thanks @rodboev. Closes #6099.
2026-07-18 20:56:44 -07:00
Rod Boev 1df8ef42cf feat(workspace): sort selector in file-tree kebab menu (#6066) 2026-07-15 07:47:26 -04:00
nesquena-hermes 503a369d0c refine(#2147): drop confusing Binding row, keep localization + Example
Maintainer revision of #5806: the localization refactor (all profile-concept
help text now goes through t() so it's translatable via the documented
English fallback) is the core value, and the concrete Example row genuinely
clarifies how profiles + workspaces work together. The Binding row restated
the Together row + added scheduled-task edge-case trivia that muddied a concept
explainer, so it's dropped (row + its 2 i18n keys + its test).

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-14 04:04:39 +00:00
nesquena-hermes 5d97c18242 fix(#2147): correct two inaccurate help-copy claims
- workspace 'scopes file access' -> 'keeps chat, terminal, and file browsing
  pointed at that project' (terminal/agent tools can access outside the workspace).
- scheduled tasks 'run under the profile that created them' -> 'run under the
  profile selected on the task, or the WebUI server default when none selected'.

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-14 03:40:21 +00:00
Rod Boev d2843d94e7 fix(#2147): restore i18n fallback for profile concept copy 2026-07-14 03:33:00 +00:00
Rod Boev 2f95ae7dbb fix(#2147): use stable label keys instead of splitting translated prose 2026-07-14 03:33:00 +00:00
Rod Boev 453b9696e3 fix(#2147): add profile_concept keys to all locale blocks and update existing test 2026-07-14 03:33:00 +00:00
Rod Boev e9b17b1a52 feat(#2147): localize profile concept help and add usage examples 2026-07-14 03:33:00 +00:00
Loukky d1aa5945b6 Update i18n.js 2026-07-13 18:57:49 +00:00
Loukky 6ccf9168f2 [locale]Localization for Simplified Chinese
Translate all untranslated English strings into Simplified Chinese.
2026-07-13 18:57:49 +00:00
f8546915ab Release exp-v0.52.55: localize manual Docker update guidance (#5973, #5959) (#5985)
* fix(i18n): localize manual Docker update guidance

* Release exp-v0.52.55: localize manual Docker update guidance (#5973, #5959)

---------

Co-authored-by: KaliShodan <25535214+jbbottoms@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 23:53:02 -07:00
9643cdd5c1 Release exp-v0.52.50: transparent-stream lazy settled DOM + per-turn cap (#5974, #5966) (#5981)
* fix(transparent-stream): defer settled tool-detail DOM + cap per-turn rows (#5966)

Reported by @curtisszmania-cytocync.

Transparent Stream built a DOM node for every activity event of every settled
turn at load, and eagerly materialized each tool row's full detail body — a
15+ prompt reasoning-heavy history produced thousands of subtrees and tipped the
tab into a multi-GB freeze. The #5860/#5839 lazy-worklog fix did not cover
Transparent Stream (it never collapses).

Two-part surgical fix in static/ui.js, mirroring #5860's proven machinery:
- Row-detail deferral: a settled+collapsed transparent tool row renders
  header-only; its heavy .tool-card-detail body builds on first expand
  (_materializeTransparentToolDetail), recovering the tool call from the scene
  after an innerHTML cache round-trip. Same anchor-suppressed post-processing as
  the eager path, so an expanded row is byte-identical.
- Per-turn row cap: a settled turn with >CAP+SLACK (30+10) rows renders the last
  30 behind a clean 'Show earlier steps (N)' affordance that materializes the
  prefix in place with viewport compensation. Just-settled turn (keep-open token)
  and already-revealed turns are exempt; true Trace tool-count is stashed so the
  label stays honest; Expand-all reveals capped rows; cache-restore re-wires the
  affordance. Live rendering is untouched.

Server-side #5858 (scene serialize-after-messages) already covers the container
RSS half on the experimental channel; this is the browser-DOM half.

* style(#5966): compact 'Show earlier steps' pill (hug content, left-aligned)

Constrain the affordance to fit-content width + align-self:flex-start so it
reads as a light inline control on the activity rail, not a full-width bar.

* fix(#5966): address Codex gate findings + Fable UX fast-follows

Codex SHIP-ONLY-WITH-FIXES (3 SILENT defects), all fixed + browser-verified:
- F1: buildToolCard PRE-BUILDS .tool-card-detail when a tool has args/output, so
  the old `!detail` defer guard skipped exactly the heavy rows. Defer now fires on
  settled+collapsed+has-detail and STRIPS any prebuilt body (verified: 400 shell
  tool rows -> 0 detail bodies mounted; expand still materializes byte-identically).
- F2: stamp owner rawIdx on rows + affordance; dataset recovery and cache-restore
  rebind resolve the scene-owning message (multi-segment turns own it on a later
  segment, not the first) with a scene-owning-segment fallback.
- F3: persist revealed-turn state in a session/owner-keyed Set + invalidate the
  session HTML cache on reveal, so a rebuild/switch-away never re-caps a turn the
  user expanded (verified: reveal 30->400, full re-render stays 400).

Fable SHIP-UX fast-follows: t()-with-fallback label (+ en locale keys), 640px
touch breakpoint, stale comment fixed.

Tests: 21 pass (added has-detail deferral+strip, detail-less no-defer, owner-idx
recovery, persistent-reveal, i18n).

* fix(#5966): address Codex re-gate findings (r2)

Codex re-gate SHIP-ONLY-WITH-FIXES — 2 more SILENT defects, both fixed + browser-verified:
- F1(r2): _materializeTransparentToolDetail rebuilt via the thinner
  _transparentToolDetailHtml(), dropping buildToolCard's richer detail (diff
  coloring / show-more / canonical shell-command lead). Now transplants
  buildToolCard(tc)'s own .tool-card-detail node so an expanded deferred row is
  byte-identical to the eager path (verified: canonical structure + 595-char body).
- F2(r2): after an in-session renderMessages() rebuild (next send) an already-open
  settled tool row got re-deferred, and disclosure-restore only toggled .open →
  open-but-empty card. _setWorklogDetailDisclosureOpen now materializes a deferred
  transparent row BEFORE toggling open (verified: expand → rebuild → still open
  WITH content). Likely the same defect the RegressionGate caught intermittently.

Tests: 22 pass (added canonical-detail + disclosure-restore-materialize).

* fix(review): add show_earlier_steps i18n keys to all locales (#5966)

The transparent-stream row-cap "Show N earlier steps" pill added two new
i18n keys (show_earlier_steps, show_earlier_step_one) to the English block
only, so the strict locale-parity tests (zh/ko/ru/zh-Hant/...) failed with
missing-key assertions and CI shard 0 went red across 3.11/3.12/3.13.

Add both keys to all 14 non-English locale blocks with the English string as
fallback (the repo's established add-with-English-fallback convention for new
UI keys). Inserted after each block's collapse_all anchor. All 197 locale
tests + the #5966 suite green; node --check clean.

* fix(review): extract _transparentToolRowHasDetail into the #5700 harness

#5966 added a _transparentToolRowHasDetail() call into the settled transparent
render (_anchorSceneTransparentNodeForRow) to decide whether to defer a
collapsed tool row's detail body. The pre-existing #5700 timestamp test extracts
that render fn into a node harness but didn't extract the new helper, so the
extracted fn hit a ReferenceError → node exited non-zero → 3 #5700 tests failed
(RED across CI shards; the PR updated its own #5966 harness but missed this one).

Add `eval(extractFunc('_transparentToolRowHasDetail'))` to the harness. The
helper self-guards its _toolActionKind/_toolCardAllowsDetail deps (returns true
when absent), and the #5700 timestamp lives on the row HEADER (rendered even when
detail is deferred), so all 20 #5700 assertions pass unchanged — confirming this
was a harness gap, not a timestamp regression.

* fix(#5974): hide Show-earlier-steps pill when a capped transparent turn is collapsed (Fable UX gate)

Fable UX gate SHIP-WITH-UX-FIXES: the .transparent-earlier-steps pill wasn't in the
.assistant-turn[data-transparent-turn-collapsed=1] hide rule, so collapsing a capped
turn via the name-tag toggle left an orphan pill. Added it to the hide rule.
(i18n finding was already resolved — both pill keys present in all 15 locales w/
English fallback + show_earlier_step_one is live at ui.js:12918, not dead.)

* Release exp-v0.52.50: transparent-stream lazy settled DOM + per-turn cap (#5974, #5966)

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-07-11 21:56:20 -07:00
6361d920ff Release exp-v0.52.49: dictation preserves composer text (#5895) (#5978)
* fix(dictation): preserve composer text when server STT commits

Dictation via the server STT path (MediaRecorder → POST /api/transcribe)
replaced any text already in the composer with the recognized transcript.
Browser SpeechRecognition worked correctly; only the async path was broken.

Root cause: the MediaRecorder.onstop handler calls _setRecording(false)
before awaiting _transcribeBlob's fetch. _setRecording(false) clears the
module-level _prefix. By the time the fetch resolves and _commitTranscript
runs, _prefix is '' — so the existing append logic collapses to
'just the transcript' and overwrites the textarea.

Browser SR dodged this because sr.onend builds the merged string into a
local variable before invoking _setRecording(false). Server STT is async
and could not use that pattern without help.

Fix:
- _commitTranscript accepts an optional prefixOverride parameter.
- _transcribeBlob snapshots _prefix into a local before the await.
- Pass the snapshot through to _commitTranscript.

Add a Settings → Voice → 'Append dictation to composer' toggle (default
ON) so users can opt into replace mode if they prefer the old buggy
behavior. The bug fix changes the default to append, matching what the
browser SR path always did.

i18n: new keys dictation_append_label / dictation_append_desc added to
all 14 locale blocks in static/i18n.js (English fallback where no native
translation exists).

Files: static/boot.js (+43/-7), static/index.html (+7), static/i18n.js (+30).

* fix(dictation): snapshot _prefix in onstop, not inside _transcribeBlob

First attempt at the append-mode fix took the snapshot inside
_transcribeBlob via 'const prefixSnapshot = _prefix'. By the time
_transcribeBlob runs (awaited from recorder.onstop), the sync block
above has already called _setRecording(false), which clears _prefix
inside _setRecording's !on branch. So the snapshot was always '' and
the append path fell through to the replace branch.

Move the snapshot into recorder.onstop, before _setRecording(false)
runs. Pass it as a parameter into _transcribeBlob so the call site
doesn't need to re-read the (now-cleared) module-level _prefix.

No behavior change for the browser-SR path — sr.onend still builds
the merged string into a local variable before invoking
_setRecording(false), and was already correct.

* fix(dictation): trim comments to fit test_590_recording_stops_before_transcribe

test_bugbatch_apr2026.py::test_590_recording_stops_before_transcribe slices
700 chars from 'recorder.onstop' and asserts that _setRecording(false)
comes before _sendRawAudio(/_transcribeBlob( inside that window. With
the verbose 6-line comment block from the previous fix, the actual
call sites landed at offsets 834/890 — past the 700-char window — so
the test's .find() returned -1 and the assertion failed mechanically.

Also: the verbose comment in _transcribeBlob contained the substring
'recorder.onstop', which made the test's first .find() land in the
comment instead of the actual handler, returning a wrong onstop_start.

Trim both comment blocks to single lines that fit within the test's
window and don't contain the literal 'recorder.onstop' substring.
Functional behavior unchanged from the previous fix; this is purely
a comment-shape adjustment to satisfy the brittle test.

* fix(tests): anchor test_590 on handler definition + widen window per review

The reviewer's fix-spec for test_590_recording_stops_before_transcribe
was more thorough than my single-shot comment trim:

  (1) Reword the _transcribeBlob comment so it doesn't contain the
      literal 'recorder.onstop' substring.
  (2) Make the test robust to the handler growing: anchor on
      'recorder.onstop=async' instead of 'recorder.onstop', and
      widen the fixed 700-char window to 1200.

Applied both. With these changes the test passes regardless of how
much comment text gets added in or around the handler in the future.
Also restored the verbose explanatory comments in both locations
(the previous a7f84027 had trimmed them to fit the brittle 700-char
window — that workaround is no longer needed).

Backward-compat check: the new test logic also passes against
origin/master (verified locally), so this change doesn't regress
the existing green-master invariant.

* fix(dictation): preserve user's mid-transcription edits in async server STT

Greptile flagged this as a 4/5 concern on the append-mode branch:
the async server-STT path appended the dictated text to the stale
snapshot (`_prefix` captured at recording start), which clobbered
any keystrokes the user made during the ~1s transcription round-trip.

Before this fix, with append mode ON:
  User typed before mic:    'Hello, '
  User typed during STT:    'Hello, from K'  (typed while waiting)
  Dictated text:            'world'
  Result:                   'Hello, world'   (lost 'from K')

After this fix:
  User typed before mic:    'Hello, '
  User typed during STT:    'Hello, from K'
  Dictated text:            'world'
  Result:                   'Hello, from K world'  (live state preserved)

The fix prefers live `ta.value` as the append base when the textarea
is non-empty, falling back to the `prefixOverride` snapshot only
when the textarea is genuinely empty (the original empty-composer path
where nothing to race with).

Adds a regression test in test_raw_audio_upload.py that asserts the
new `ta.value ||` fallback structure on `_commitTranscript`.

* fix(dictation): don't resurrect cleared text on async transcription resolve

Greptile P1 (re-reviewed at 20:07 UTC): the previous race fix
(`ta.value || (prefixOverride ?? _prefix)`) treats an empty
`ta.value` as falsy and falls back to the recording-start snapshot.
If the user explicitly clears the textarea while server transcription
is in flight, the cleared text reappears with the dictated text
appended — the user's intent is violated.

Fix: when prefixOverride IS passed (server-STT path), trust live
ta.value UNCONDITIONALLY — even when empty. The fallback to _prefix
only fires when no prefixOverride is given AND ta.value is empty,
which is purely defensive (no current caller hits this branch — the
browser-SR path commits inline in sr.onend without calling this
function).

Concrete repro:
  User typed before mic:    'Hello, '
  User clears textarea during STT
  Dictated text:            'world'
  OLD (0239bb4e) result:    'Hello, world'  (cleared text resurrected)
  NEW result:               'world'        (user intent respected)

Greptile's suggested one-liner:
  const base = prefixOverride !== undefined ? ta.value : (ta.value || _prefix);
Applied with expanded inline comment documenting the three concerns
this function balances (race, clear-during-STT, defensive fallback).

* Release exp-v0.52.49: preserve composer text on server STT commit (#5895)

---------

Co-authored-by: Konstantin M <kopamedve@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 20:47:54 -07:00
99ca1d2d73 Release exp-v0.52.46: fix public-share locale value swap (#5955) (#5968)
* Fix public share locale values

* Release exp-v0.52.46: fix public-share locale value swap (#5955, regression from #5778)

---------

Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 18:19:52 -07:00
18793a1ba4 Release exp-v0.52.42: ?profile= deep links (#5730) + Russian locale refresh (#5778) (#5953)
* Refresh Russian localization bundle

* feat(#5682): honor profile query during boot

* fix(profile): refresh deep-link reasoning state (#5682)

* test(ci): initialize reasoning transition harness state

* test(ci): accept reasoning fetch context override

* fix(#5682): preserve retryable profile query intent

* test(#5682): accept profile switch result contract

* fix(#5682): bind reasoning fetches to profile transitions

* Release exp-v0.52.42: ?profile= deep links (#5730) + Russian locale refresh (#5778)

---------

Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 15:58:47 -07:00
nesquena-hermesandruizanthony 54efd6078e feat(workspace): opt-in 'start new chat when switching workspace' (#5473)
Salvage-rebuild of #5473 (@ruizanthony), reworked to OPT-IN per maintainer
decision. New default-OFF setting new_chat_on_workspace_switch: when enabled,
selecting a DIFFERENT workspace starts a fresh chat bound to that workspace
(leaving the current conversation on its original workspace, avoiding stale
cross-workspace context) instead of mutating the current session in place.
Same-workspace selection stays an in-place refresh. Default off preserves the
shipped behavior for everyone.

Wiring: config default + bool key; window._newChatOnWorkspaceSwitch set on
boot + autosave; Settings checkbox (Preferences); gated branch in
switchToWorkspace() that runs before the in-place /api/session/update; 3 i18n
keys across all 15 locales; regression tests (default-off, gated-branch-present,
flag-wiring, checkbox+i18n).

Co-authored-by: ruizanthony <ruizanthony@users.noreply.github.com>
2026-07-11 03:13:48 +00:00
nesquena-hermes 55509e98c2 i18n(cs): add share_session/stop_sharing keys for Czech locale parity
Czech (#5546) was added after #5337 authored its share i18n keys, so the
strict cs-parity test failed on the 14 missing share_session/stop_sharing keys.
Added them to the cs block (English fallback values per locale convention).
2026-07-11 02:37:57 +00:00
nesquena-hermes e061e8534c Rebase #5337 (public read-only conversation sharing) onto master
Rebased 363 commits forward (branch-point 2026-07-05). Applied the PR's code
diff cleanly across 16 files; resolved api/models.py additively (both master's
process_wakeup_pause/anchor_scene_index fields AND the PR's share_token/
share_created_at fields kept in the Session constructor, metadata list, and
to_dict). CI workflow (.github/workflows/tests.yml) intentionally NOT taken from
the stale branch — master owns it (the branch's apparent removal of Playwright/
Office steps was a PAT-workflow-scope revert; rebase restores master's CI).

Co-authored-by: MicoRobot. Concept approved by maintainer (opt-in public share,
2026-07-11). Note: 4 dynamic share tests fail identically on the original
unrebased head (pre-existing test-env Session.load fragility, not rebase-caused);
to be resolved during the security re-gate in a proper sandbox.
2026-07-11 01:39:42 +00:00
nesquena-hermes 0af0a5fcf0 Merge #5746: final answer only activity display mode 2026-07-10 23:50:28 +00:00
de0e6eb452 Release: centralize auxiliary-model task catalog (#5865) (#5870)
Auxiliary-model task definitions were maintained in BOTH Python and JS and could
drift (the retired session_search task lingered in one). Centralize to a single
backend catalog that panels.js renders+saves from exclusively; purge the retired
slot on reset while preserving other custom bindings; old configs still load.

Gate-pass: Codex SAFE (only session_search removed, 11 other tasks + defaults
unchanged, 22 keys match across 15 locales, legacy migrate preserves custom
bindings, executable DOM harness rendered all rows). 53 own + 887 regression green.

Co-authored-by: t <a@b>
Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-07-09 20:31:54 -07:00
8cc2d6f881 Release: model-picker Selected badge + Transparent Stream dupe-prefix suppression (#5757, #5758) (#5850)
Two visible render PRs, gate-passed (deep-review round), Nathan-approved to ship.

- #5757 (rodboev): mark active model with a Selected badge + open its provider group (#5748).
  Screenshot-verified (badge clear + correctly placed on active row).
- #5758 (rodboev): suppress the live-token prose row that is a strict prefix of the final
  answer (#5749). Provenance-gated (live-prose:* token rows after last tool row only) so it
  can't eat legitimate narration; reproduction test proves the dupe is gone.

Codex SAFE + full regression green (1576 render tests). 18 own tests pass together.

Co-authored-by: t <a@b>
Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-09 12:51:18 -07:00
Isla-Liu db082e8d92 fix: localize process wakeup label 2026-07-09 04:36:06 +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
t 0a56f44f5e fix(updates): Fable UX gate — channel re-check race + copy + mobile badge
MUST-FIX: /api/updates/check now accepts an explicit 'channel' in the POST body;
the Settings dropdown passes the just-picked value to checkUpdatesNow(channel) so
the immediate re-check can't race the debounced autosave PUT and answer for the
previous channel (verified live: body channel wins over stale saved setting).

SHOULD-FIX:
- copy: 'soaked' -> 'soak-tested'; append 'Applies to WebUI updates only.' to the
  channel helper text (all 15 locales, native translations)
- mobile: .settings-version-badge gets max-width/overflow/ellipsis in the <=768px
  query so the '· Experimental' suffix can't overflow a 320px viewport

(#5 badge-suffix i18n key deferred — non-blocking fast-follow per Fable, matches
the existing English-only version badge on master.)
2026-07-08 20:01:39 +00:00
t bf5e5a0d3d test(updates): fix channel-signature fallout + add i18n keys to all 14 locales
- test_update_stash_recovery: add merge-base handler for the force-update
  rewind guard probe (origin/master is a descendant of HEAD -> not a rewind)
- test_security_review_fixes: fake_check accepts channel kwarg
- i18n.js: native translations of the 4 update-channel keys for all 14
  non-English locales (locale-coverage tests enforce key presence)
2026-07-08 19:37:44 +00:00
t 2cf43527a3 feat(updates): stable/experimental release channels
Add an update_channel setting (stable|experimental) that selects which git-tag
stream the self-updater tracks on the single linear master line:

  stable       -> 'v*'      promoted, soaked releases (default; unchanged glob)
  experimental -> 'exp-v*'  every release batch (opt-in testers)

A channel is only a tag glob — no branches, no divergence — so every ff-only
guarantee (#2653/#2846/#3140) is preserved. Channel governs the WebUI repo only;
the Agent repo keeps its historical branch fall-through.

Correctness (advisor-reviewed, Codex + Fable):
- describe uses --match <glob> so a commit tagged both v* and exp-v* resolves
  to the channel-correct tag
- stable never falls through to origin/master when HEAD contains the latest
  stable tag (the firehose-suppression that makes channels work)
- apply_force_update refuses to reset --hard onto an ancestor ref (rewind guard)
- update cache + in-progress guard keyed by (channel, include_agent)
- channel display badge is a SEPARATE field; WEBUI_VERSION stays channel-neutral
  (asset cache-busting / SW cache / stale-client skew all do exact-string equality)

Settings UX: channel dropdown with risk-forward copy + the ff-only asymmetry
helper ('switching back to Stable keeps your current version until Stable catches
up'), channel chip on the version badge, i18n keys, re-check on switch.

Tests: new tests/test_update_channels.py (12 real-git-fixture tests) + existing
update suite updated for the channel-aware signatures.
2026-07-08 19:25:27 +00:00
rodboev 8b02a8c260 fix(#5723): complete stale-busy cleanup on dead-run recovery (Fable UX-gate)
_steerClearCurrentOwnerDeadRun cleared busy/activeStreamId/inflight but left
the status line, elapsed timer, and optimistic streaming badge lingering after
a recovered dead run. Add the rest of the app's stale-busy cleanup idiom
(setStatus/setComposerStatus/_clearActivityElapsedTimer/clearOptimisticSessionStreaming),
still bypassing setBusy(false) so its queue-drain can't clobber the restored draft.
2026-07-07 16:32:26 +00:00
nesquena-hermesandrodboev 2717ea0ac5 feat(chat): add final answer only activity display mode (#5706, picker grid fixed)
Clean rebase of rodboev's #5746 (rebase-first).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-07 14:38:08 +00:00
ostravajih 6da81f7a30 feat(i18n): add Czech (cs) locale support
Add a complete Czech (cs) locale to hermes-webui: full key parity with the
English reference (1642/1642 keys), real Czech translations for all string and
function-valued keys, Slavic plural helpers for tool/worklog summaries, the cs
login-screen locale in api/routes.py, and a dedicated tests/test_czech_locale.py
parity+placeholder+diacritics guard mirroring the other per-locale tests. All
15 locale-count assertions bumped 14->15.

Co-authored-by: ostravajih <ostravajih@users.noreply.github.com>
2026-07-06 21:27:46 +00:00
nesquena-hermesandRod Boev 936c5b051b release: redesign cron preset schedule builder — sentence UI with time picker + day dropdowns (#5554)
Reworks the #5552 preset builder per maintainer UX feedback + Fable design advisory: one natural 'Schedule: [preset] on [day] at [time]' row; native time picker (themed) replacing hour/minute number boxes; day-of-week + day-of-month dropdowns; raw cron field shown only on Custom; live cron preview; Custom last; new jobs default to Daily 09:00; keystroke-clamp + mid-typing-hide bugs fixed. New i18n keys across all 14 locales. Full gate: Codex SAFE, Fable SHIP-UX, suite 12160/0, browser clean.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-06 01:35:55 +00:00