Commit graph

7619 commits

Author SHA1 Message Date
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
nesquena-hermes
942e952849
Release: fix Kanban column scrolling on mobile (#6306, @jpalazz2) (#6347)
* fix: Fix kanban scrolling in mobile viewports

Scrolling vertically (particularly in expanded view) in kanban on
a mobile viewport is difficult. The columns have overscroll
disabled, meaning tap and drag will only scroll within the column
and will not continue to the next section. On desktop it's much
easier to get the mouse outside the column div, on mobile you have
to deliberately try to tap very close to the edge of the viewport.

Disabling that behavior makes the experience much better

Author: Joe Palazzolo <joe@joepalazzolo.net>

* docs(changelog): stamp #6306 mobile Kanban column scroll fix

---------

Co-authored-by: Joe Palazzolo <joe@joepalazzolo.net>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 04:15:33 +00:00
nesquena-hermes
02b2b0aac1
Release: Live Stream stable Anchor run identity (#6201, @franksong2702) (#6346)
* fix: preserve live anchor run identity

* Validate envelope run ids before snapshot cursor use

* docs(changelog): stamp #6201 stable Anchor run identity

---------

Co-authored-by: Frank Song <franksong2702@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 04:10:06 +00:00
Frank Song
b1dcdd6ec8 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 10:25:19 +08:00
nesquena-hermes
98a67ee6cf
Release: pip-installable packaging metadata (#6337, @rodboev) (#6344)
* build(#2695): add packaging metadata for the current runtime layout

* docs(changelog): stamp #6337 pip-installable packaging metadata

---------

Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 02:17:52 +00:00
Frank Song
80d40d6531 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 09:30:56 +08:00
nesquena-hermes
4d9965b37a
Release: content search no longer evicts the working-set cache (#6084, @ai-ag2026) (#6343)
* fix(webui): keep the content search from evicting the user's working set

/api/sessions/search?content=1 walks EVERY session and pulls each one through
get_session(), which inserts it into the SESSIONS LRU and marks it
recently-used. On any install with more sessions than sessions_cache_max
(default 300), a single search therefore flushes the whole cache and refills it
with sessions the user is not looking at — the classic buffer-pool
scan-pollution problem. The sessions actually open in the UI are exactly the
ones evicted, and the search is keystroke-debounced, so it repeats while typing.

A scan reads each session exactly once, so nothing it touches has earned
"recently used". get_session_for_scan() reuses a resident session without
promoting it, and reads a cold one straight from disk without caching it. It
returns None rather than raising, since a scan skips what it cannot open.

This is a correctness fix for cache behaviour, not a latency fix. The
multi-second searches that led here were contention, not scan cost: a trivial
/api/profiles took 9.2s in the same window, and a full read+parse of ~1700 real
sessions measures ~4s total.

test_sessions_search_depth_validation patched api.routes.get_session. With the
search reading through the scan accessor it now patches get_session_for_scan —
left unfixed, two of its cases fail and the third passes vacuously against an
empty result set.

Validation:
  pytest tests/test_issue4765_sessions_lru_eviction.py
         tests/test_sessions_search_depth_validation.py   ->  13 passed
  Both added eviction tests fail on the pre-fix accessor (verified by revert):
  the working set drops from 4/4 to 0/4 resident after one scan.

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

* docs(changelog): stamp #6084 content-search working-set preservation

---------

Co-authored-by: ai-ag2026 <261867348+ai-ag2026@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 01:21:18 +00:00
Frank Song
7b0ef79541 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 06:18:07 +08:00
nesquena-hermes
307b7fce5f
Release: terminal-error settlement timing/seal (#6323) + Docker experimental builds (#6329) (#6341)
* fix: preserve timing and seal tool rows on terminal error (#6309)

* fix: publish Docker experimental builds to ghcr (#6298)

- Add exp-v* trigger to release workflow so experimental tags build
  and push Docker images
- Add :experimental floating tag for experimental channel, keeping
  :latest scoped to stable v* tags only
- Mark GitHub Releases from exp-v* tags as pre-releases
- Document available Docker tags (:latest, :experimental, version
  pins) in docs/docker.md

Closes #6298

* docs(changelog): stamp #6323 terminal-error timing/seal + #6329 Docker exp builds

---------

Co-authored-by: webtecnica <webtecnica@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-19 22:17:36 +00:00
nesquena-hermes
10e5c52b10
Release: broadcast terminal output to every viewer (#5836, @ai-ag2026) (#6340)
* feat(terminal): broadcast output to every viewer instead of one shared queue

TerminalSession.output was a single queue.Queue read destructively by the SSE
handler. Two tabs/windows viewing the SAME session each open their own
EventSource, so two _handle_terminal_output handlers competed on that one queue:
every PTY chunk was delivered to exactly one of them. Each tab saw a disjoint
half of the byte stream, and only one ever received terminal_closed.

Output now fans out, mirroring StreamChannel/SessionChannel: each SSE consumer
subscribe()s its own queue (seeded with a bounded backlog so a first/late attach
still replays the recent scrollback, preserving the old buffer-until-first-
consumer behaviour), and put_output broadcasts to all subscribers. A slow
viewer's queue drops its own oldest chunk (drop-oldest, isolated per subscriber)
so one lagging tab can't starve another. The handler unsubscribes in a finally
so the subscriber list can't grow.

Stacked on the terminal fd-leak fix (same file).

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

* fix: cover terminal broadcast lifecycle

* fix: serialize terminal subscriber fanout

* test: cover terminal unsubscribe publication race

* restore timing-flaky pytest.skip on test_terminal_survives_short_lived_request_thread (keep API-updated body); changelog #5836

---------

Co-authored-by: ai-ag2026 <261867348+ai-ag2026@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-19 22:08:29 +00:00
Frank Song
223427741f Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-20 02:02:13 +08:00
nesquena-hermes
4e768fe247
Release: gateway approval_id fallback (#6168) + update-check guard (#6180) + state-dir test isolation (#6305) (#6332)
* fix: catch unhandled exception in POST /api/updates/check (defensive hardening) (#6180)

* fix: generate non-empty approval_id when gateway approval.request omits it (#6008) (#6168)

* chore: mark update-check try/except as defensive-only guard, drop #6086 linkage

Per maintainer review, the try/except wrapper is defense-in-depth only —
it does NOT fix #6086 (root cause is signal/process-group reaping).
Updated log message and added inline comment to make this explicit.
Leave #6086 open.

* test: isolate state-dir probes from user state

* docs(changelog): stamp #6168 approval_id, #6180 update-check guard, #6305 test isolation

---------

Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>
Co-authored-by: webtecnica <webtecnica@users.noreply.github.com>
Co-authored-by: pxxD1998 <214340659+pxxD1998@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-19 17:41:07 +00:00
Frank Song
7e0582bfcf Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-19 12:30:02 +08:00
Frank Song
deac1384fe
Live Stream: add public conversation lifecycle browser gate (#6251)
* test: add conversation lifecycle browser gate

* test: wait for durable lifecycle settlement

* test: harden lifecycle gate cleanup and startup

* test: normalize gateway fixture request paths

* test: harden conversation lifecycle gate

* test: fix lifecycle request failure capture

* test: align lifecycle CI dependencies

* test: harden lifecycle gate waits

* docs: align lifecycle gate setup command

* test: harden lifecycle gate persistence wait

* ci: scope conversation-lifecycle gate to relevant code paths

Only run the playwright browser gate when the chat render/streaming surface
it exercises actually changes (static/**, api/**.py, server.py, the test,
deps, the workflow). Docs-only and unrelated PRs skip it entirely, keeping
CI lean per the docs-only fast-path philosophy.

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

---------

Co-authored-by: Frank Song <franksong2702@gmail.com>
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-07-18 21:23:25 -07:00
Frank Song
abdb0cbba0 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-19 12:00:03 +08:00
nesquena-hermes
31d4281a19
Release: optional Transparent Stream event timestamp chips (#6130, @rodboev) (#6300)
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-07-18 20:58:41 -07:00
Frank Song
60017c76a2 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-19 11:58:29 +08: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
Frank Song
8ff17ea7bb Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-19 11:18:17 +08:00
nesquena-hermes
17c2f2086c
Release: bg-process restart recovery (#6287) + Windows test-fixture symlink fallback (#6276) (#6294)
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-07-18 20:10:01 -07:00
allenliang2022
ce048b0a5b
fix(wakeup): recover terminal process completions after restart (#6287)
Recover checkpointed core background processes and rebuild PROCESS_SESSION_INDEX on WebUI startup, so an ordinary terminal(background=True, notify_on_complete=True) proc_* completion that outlives a WebUI restart can still wake its original session. Complements #6283 (which covered async_delegation completions). Thanks @allenliang2022.
2026-07-18 20:07:54 -07:00
Frank Song
bf961cea9d Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-19 11:02:47 +08:00
Rod Boev
704e19adfe
fix(#6240): fall back when test skills symlink is unavailable (#6276)
Fall back to a copytree (with read-only handling) when the test-server fixture can't create the skills symlink on native Windows without SeCreateSymbolicLinkPrivilege (WinError 1314). Test-infra only. Thanks @rodboev. Closes #6240.
2026-07-18 19:56:06 -07:00
Frank Song
0ae40dcd98 Merge remote-tracking branch 'origin/master' into paperclip/pr-6222 2026-07-19 10:42:31 +08:00
nesquena-hermes
6ad84d7dd3
fix(wakeup): route async-delegation completions by origin + durable-claim delivery (#6283)
Route async-delegation completions by the immutable origin_ui_session_id (exact origin tab) and deliver them through a durable claim/complete/release lifecycle so they arrive exactly once, restart-safe, on both the background wakeup and next-turn drain paths. Combines #6185 (@carlotestor) + #6159 (@sysophelper-droid); supersedes #6002/#6225.

Co-authored-by: carlotestor <carlotestor@users.noreply.github.com>
Co-authored-by: sysophelper-droid <sysophelper-droid@users.noreply.github.com>
2026-07-19 02:26:55 +00:00