Commit Graph
5163 Commits
Author SHA1 Message Date
nesquena-hermes ff110458fd Release WV (v0.51.641): dark-mode disabled-plugin badge contrast + active-first sort (#4848, closes #4713)
Release WV (v0.51.641): dark-mode disabled-plugin badge contrast + active-first sort (#4848, closes #4713)
2026-06-24 20:20:46 -07:00
nesquena-hermes d59e60235f Release WV (v0.51.641): dark-mode disabled-plugin badge contrast + active-first sort (#4848, closes #4713) 2026-06-25 03:17:51 +00:00
Rod Boev c109ed9113 fix(plugins): align activation-state sort with badge precedence (#4713) 2026-06-25 02:57:06 +00:00
Rod Boev e3a1540d32 fix: align sort partition with badge for inactive exclusive providers 2026-06-25 02:57:06 +00:00
Rod Boev 7c1cd99961 fix(#4713): dark-mode disabled badge contrast and active-first plugin sort 2026-06-25 02:57:06 +00:00
nesquena-hermes 98e13bc92a Release WU (v0.51.640): cache Claude Code transcript parses on the profile-switch path (#4718, #4662)
Release WU (v0.51.640): cache Claude Code transcript parses on the profile-switch path (#4718, #4662)
2026-06-24 19:51:14 -07:00
nesquena-hermes f7788d9d5a Release WU (v0.51.640): cache Claude Code transcript parses on the profile-switch path (#4718, #4662) 2026-06-25 02:47:03 +00:00
nesquena-hermes d61ca37bd1 fix(#4718): harden parse-cache key with ctime_ns + preserve epoch-0 mtime fallback
Dual-gate (Codex + Opus) findings on the Claude Code transcript parse cache:

1. Cache-key staleness hardening: add st_ctime_ns to the (path, mtime_ns, size,
   max_messages) key so a rare same-size, same-mtime in-place edit can't serve a
   stale parse. Size is already the strong discriminator for append-only JSONL;
   ctime_ns is cheap defense-in-depth (a spurious bump costs one harmless re-parse,
   never a stale read).

2. Timestamp-fallback bug-for-bug fix: the row-builder guard used identity
   (first_ts is None and last_ts is None) but the field assignment uses truthiness
   (first_ts or last_ts or _mtime). They disagreed on a falsy 0.0 timestamp
   (epoch-0/1970 transcripts), leaving created_at/updated_at/last_message_at = None
   instead of the file mtime the pre-cache inline code fell back to. Guard now uses
   the same 'not first_ts and not last_ts' test.

Adds 3 regression tests: epoch-0 mtime fallback, the shared-dict read-only
contract (pins the load-bearing invariant), and same-size+mtime ctime invalidation.
2026-06-25 02:46:00 +00:00
nesquena-hermesandrodboev fdb734a6a0 perf(#4718): cache Claude Code transcript parses on the sidebar path
The /api/sessions cold build (which a profile switch triggers) was
dominated by get_claude_code_sessions() re-parsing every Claude Code
JSONL transcript line-by-line on each call — 650-1000ms on a 200-file
/ ~130MB tree, repeated on every switch, every 5s CLI-cache expiry, and
every sidebar poll, because the directory is global but the higher
session cache is keyed per active profile.

Memoize the per-file parse by (path, mtime_ns, size) so a warm build
re-stats files instead of re-parsing them; any genuine edit/append
changes the stat signature and invalidates just that file. Also hoist
the loop-invariant get_last_workspace() out of the Claude Code row loop
(it stats config + probes terminal cwd, ~200 redundant calls per build).

Measured /api/sessions cold-rebuild on a 250-session profile: ~670-880ms
-> ~100-200ms (verified in-browser). Output byte-identical.

Credit @rodboev for the browser-level profiling that isolated the real
bottleneck (the prior pre-warm hypothesis could not move it).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-25 02:45:59 +00:00
nesquena-hermes 703d44b168 Release WT (v0.51.639): persist + session-scope approval-card dismissal (#4846, closes #4754)
Release WT (v0.51.639): persist + session-scope approval-card dismissal (#4846, closes #4754)
2026-06-24 19:43:44 -07:00
nesquena-hermes 5c052b59bb Release WT (v0.51.639): persist + session-scope approval-card dismissal (#4846, closes #4754) 2026-06-25 02:40:22 +00:00
nesquena-hermesandRod Boev 0a6947c2d2 fix(#4754): namespace dismissed approvals by session (gate finding)
Codex CORE: the dismissed-set was keyed by bare approval_id, but gateway/run
sources preserve externally-supplied IDs without session namespacing — so two
sessions carrying the same approval_id collide and dismissing in session A hides
session B's still-pending approval. Fix: _approvalDismissKey(sid, approvalId)
builds a session-namespaced composite key, threaded through _isApprovalDismissed,
_markApprovalDismissed, _unmarkApprovalDismissed + all 5 call sites (showApprovalCard,
dismissApprovalCard, respondApproval, no-pending poll cleanup). Updated 4 stale
assertions to the namespaced signatures + added a functional node-driver
cross-session-collision regression (proven non-vacuous).

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-25 02:15:47 +00:00
Rod Boev 0258599a46 fix(approval): clear pending attention state on dismiss (#4754) 2026-06-25 02:04:51 +00:00
Rod Boev 2764e28b95 fix: remove unused json and re imports 2026-06-25 02:04:51 +00:00
Rod Boev 820362ca14 fix: scope un-dismiss to session-local pending, not global _approvalCurrentId 2026-06-25 02:04:51 +00:00
Rod Boev 2956804a14 fix(#4754): persist approval card dismissal across tab switches and restarts 2026-06-25 02:04:51 +00:00
nesquena-hermes b66356ed67 Release WS (v0.51.638): bound continuation-fallback sidecar reads (#4882)
Release WS (v0.51.638): bound continuation-fallback sidecar reads (#4882)
2026-06-24 19:01:23 -07:00
nesquena-hermes 04fb59ac65 Release WS (v0.51.638): bound continuation-fallback sidecar reads (#4882) 2026-06-25 01:58:16 +00:00
nesquena-hermesandstarship-s 5296b15956 fix(session): preserve 4096-CHAR prefix semantics in bounded sidecar read (gate finding)
Codex reproduced a SILENT regression: the PR's 4096-BYTE read could miss the
continuation marker the old read_text()[:4096] (4096 CHARS) found. Session.save()
writes compression_anchor_summary (ensure_ascii=False) BEFORE parent_session_id,
so a multi-byte emoji summary puts the marker at char offset ~1230 but byte
offset ~4900 — within the old 4096-char window, past a 4096-byte one → continuation
child no longer detected. Fix: read 16384 bytes (4096 chars worst-case for 4-byte
UTF-8) then slice [:4096] chars, keeping the same needle + bounded I/O. Multibyte
regression test added + proven non-vacuous; updated the read-size assertion to <=16384.

Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-06-25 01:49:11 +00:00
Hermes Agent ddec907a03 test(session): delegate wrapped file context entry 2026-06-25 01:40:22 +00:00
Hermes Agent dd67f342d7 fix(session): bound continuation fallback sidecar reads 2026-06-25 01:40:22 +00:00
nesquena-hermes 031856b3b5 Release WR (v0.51.637): widen continuation-prompt filter to all 3 variants (#4880, closes #4875)
Release WR (v0.51.637): widen continuation-prompt filter to all 3 variants (#4880, closes #4875)
2026-06-24 18:37:36 -07:00
nesquena-hermes 7d460da1ea Release WR (v0.51.637): widen continuation-prompt filter to all 3 variants (#4880, closes #4875) 2026-06-25 01:33:45 +00:00
Rod Boev 7873e7ccf0 test(#4875): update live-stream-ux assertion to match widened filter 2026-06-25 01:21:08 +00:00
Rod Boev 36742abcee fix(#4875): widen continuation-prompt filter to cover output-length and tool-call variants 2026-06-25 01:21:08 +00:00
nesquena-hermes 9d8e5d6de4 Release WQ (v0.51.636): suppress Android Chromium scroll re-anchoring during DOM rebuild (#4878, closes #4856)
Release WQ (v0.51.636): suppress Android Chromium scroll re-anchoring during DOM rebuild (#4878, closes #4856)
2026-06-24 18:20:38 -07:00
nesquena-hermes 237b2b281c Release WQ (v0.51.636): suppress Android Chromium scroll re-anchoring during DOM rebuild (#4878, closes #4856) 2026-06-25 01:16:55 +00:00
Rod Boev f43bf2df6a docs(#4856): update stale call-site comment to match overflow-anchor semantics 2026-06-25 01:06:09 +00:00
Rod Boev 0668cfa77c fix(#4856): suppress Chromium scroll-anchor re-selection during mobile DOM rebuild 2026-06-25 01:06:09 +00:00
nesquena-hermes 87fb8ed9ec Release WP (v0.51.635): drop noisy parse-failed note on JSON/YAML fragments (#4858, #484)
Release WP (v0.51.635): drop noisy parse-failed note on JSON/YAML fragments (#4858, #484)
2026-06-24 15:28:30 -07:00
nesquena-hermes 71a436c607 Release WP (v0.51.635): drop noisy parse-failed note on JSON/YAML fragments (#4858, #484) 2026-06-24 22:24:38 +00:00
Charles Bine 867471c7d3 fix(#484): drop noisy "parse failed" note on JSON/YAML fragments
LLMs routinely emit JSON/YAML *fragments* in fenced blocks — a bare
`"key": "value"` line, snippets with `...`, trailing commas — which
legitimately fail JSON.parse. initTreeViews() stamped an unstyled
`<div class="tree-parse-note">parse failed</div>` below such blocks,
producing bare white text under an otherwise-clean code box.

The note was incidental, not designed: it was bolted onto the YAML
lazy-load commit (29a31a6e), has no CSS, no behavior test, and originally
even fired on *valid* YAML before the CDN script finished loading (later
fixed). The block still renders as syntax-highlighted raw, so the note
added only noise.

Remove the note insertion; fall through silently to raw view. The per-block
Raw/Tree toggle, YAML lazy-load, and the #484 default-view config are all
unchanged. The `parse_failed_note` i18n key is intentionally retained (kept
green by test_chinese_locale.py) to avoid touching all 13 locales.

Verification: ./scripts/test.sh tests/test_issue484_json_tree_viewer.py
tests/test_chinese_locale.py — 29 passed.
2026-06-24 22:14:27 +00:00
nesquena-hermes 7043b45f5d Release WO (v0.51.634): cache profile skill stats with mtime probe (#4847, closes #4783)
Release WO (v0.51.634): cache profile skill stats with mtime probe (#4847, closes #4783)
2026-06-24 15:11:46 -07:00
nesquena-hermesandRod Boev 97372c8207 test(#4783): remove un-portable node_modules-prune filesystem test
The buried far-future mtime kept bleeding into the probe value on CI's shard-0
runners despite repeated attempts to make it deterministic. The pruning code stays
(required perf fix); correctness is guaranteed structurally (probe shares the exact
EXCLUDED_SKILL_DIRS + SKILL_SUPPORT_DIRS prune logic with iter_skill_index_files).

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 22:07:40 +00:00
nesquena-hermesandRod Boev 87782a9d68 test(#4783): pin legitimate tree mtime so the prune test is filesystem-timing independent
CI shard-0 still flaked: float int(far_future*1e9) rounding left probe ~38ns above
the threshold because the legitimate vendored/ tree's mtime on CI tracked the
buried-tree creation. Now pin skills_dir/skill/SKILL.md/config.yaml to a known
modest mtime AFTER building the buried far-future node_modules subtree, and assert
probe < far_future - 1s. Deterministic across filesystems; still non-vacuous
(fails with prune disabled even when node_modules itself is stamped far-future).

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 22:03:15 +00:00
nesquena-hermesandRod Boev 41687073d6 test(#4783): make node_modules-prune test deterministic (CI fix)
The before/after assertion was fragile — creating node_modules between the two
probe calls bumped the (non-pruned) vendored/ ancestor mtime, so 'after==before'
failed on CI's filesystem timing. Rewrote to a timing-independent assertion: a
FAR-FUTURE mtime buried inside the pruned node_modules/ must NOT appear in the
probe value (probe < far_future_ns), which holds even if node_modules itself is
bumped, because the prune removes it from dirnames before the stat loop.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 21:56:26 +00:00
nesquena-hermes e17ace2c5d Release WO (v0.51.634): cache profile skill stats with mtime probe (#4847, closes #4783) 2026-06-24 21:51:00 +00:00
nesquena-hermesandRod Boev af1649f558 test(#4783): update test_skills_stats_cache to assert the fixed mtime-probe behavior
The pre-existing test asserted the OLD bug: 'add a skill within the 8s TTL ->
still get stale (1,1)'. That stale window is exactly what #4783 fixes — adding a
skill bumps the skills-dir mtime, the per-call probe catches it, and counts now
update immediately (2,2). Updated the assertions to the corrected contract +
added a no-change-serves-cache step. (Test lived in test_profile_skills_stats.py,
not the PR's test_issue4783 file, so the contributor missed it.)

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 21:41:15 +00:00
nesquena-hermesandRod Boev 3fbc7d71d3 fix(#4783): prune dependency trees in mtime probe to match compute path (gate finding 4)
Codex round-4 found my followlinks=True change introduced a perf regression: the
probe os.walk lacked the .git/.venv/node_modules/site-packages + skill-support-dir
pruning that iter_skill_index_files applies, so a skill vendoring a dependency
tree made this EVERY-CALL probe walk thousands of irrelevant files — defeating the
cache's whole purpose. The probe now prunes dirnames[:] with the same
EXCLUDED_SKILL_DIRS + SKILL_SUPPORT_DIRS sets as the compute path. Regression test
(change deep inside node_modules/ must not move the probe) added + non-vacuous.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 21:27:22 +00:00
nesquena-hermesandRod Boev e4e3b46830 fix(#4783): mtime probe follows symlinked skill dirs to match compute path (gate finding 3)
Codex round-3 reproduced: _skill_tree_max_mtime_ns used os.walk(followlinks=False)
while the compute path iter_skill_index_files uses followlinks=True, so an edit to
a symlinked skill's target SKILL.md was invisible to the probe (stale up to TTL).
Added followlinks=True to the probe walk so it descends symlinked skill dirs and
sees target mtime changes. Regression test added + proven non-vacuous.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 21:21:57 +00:00
nesquena-hermesandRod Boev 8a46d88daf fix(#4783): TTL expiry always recomputes (safety net for mtime-preserving changes) (gate finding 2)
Codex re-gate: my prior 'TTL-expired + unchanged-mtime -> refresh TTL, no recompute'
branch could leave counts stale INDEFINITELY for an mtime-PRESERVING out-of-band
change (e.g. a git checkout restoring the old mtime) — worse than master's 8s
recompute. Codex proved it (cached (2,2) stayed (2,2) while a fresh compute returned
(1,2)). Now: within TTL + unchanged mtime serves cached (perf win kept); mtime
changed OR TTL expired both force a full recompute (the TTL is a genuine safety
net). Updated the row-2 test to assert the safety recompute.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 21:16:16 +00:00
nesquena-hermesandRod Boev 7d38b0cd9f fix(#4783): probe skill mtime on every call so out-of-band changes aren't hidden for the full TTL (gate finding)
Codex gate found a SILENT regression: the cache returned on a zero-I/O fast path
while inside the 300s TTL, so _skill_tree_max_mtime_ns never ran during the TTL
window and an out-of-band (CLI/git) skill create/edit/delete was invisible for up
to 5 minutes — WORSE than master's ~8s stale window. Restructured so the cheap
stat-only mtime probe runs on EVERY call (prompt change detection); the TTL now
only bounds the expensive SKILL.md recompute. Unchanged mtime still serves cached
+ refreshes TTL with no recompute (the perf win is preserved). Updated 2 tests
that encoded the flawed zero-I/O design + added a within-TTL-change regression
(proven non-vacuous).

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 21:08:19 +00:00
Rod Boev fdf9adbd02 Catch nested skill deletions in the mtime probe 2026-06-24 20:52:31 +00:00
Rod Boev c7d156c570 fix(profiles): snapshot mtime before compute to prevent TOCTOU stale cache (#4783) 2026-06-24 20:52:31 +00:00
Rod Boev 9ce8deff5d fix: remove unused importlib and os imports 2026-06-24 20:52:31 +00:00
Rod Boev 970345f74d fix: restore sys.modules after test to prevent api package pollution 2026-06-24 20:52:31 +00:00
Rod Boev 0d6932dcce fix: invalidate skills cache on toggle/save/delete and stat skills_dir for deletion detection 2026-06-24 20:52:31 +00:00
Rod Boev 18528a88f0 perf(#4783): two-tier mtime cache for profile skills stats 2026-06-24 20:52:31 +00:00
nesquena-hermes d13da425e9 Release WN (v0.51.633): collapsed tool cards preview result not call args (#4851, closes #4752)
Release WN (v0.51.633): collapsed tool cards preview result not call args (#4851, closes #4752)
2026-06-24 13:49:19 -07:00
nesquena-hermes 2de2599e8c Release WN (v0.51.633): collapsed tool cards preview result not call args (#4851, closes #4752) 2026-06-24 20:45:30 +00:00