Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
5647 commits
Select commit Hold shift + click to select a range
adcdf9d
perf(state): batch compression-tip row fetch in list_sessions_rich
Jul 6, 2026
b2e1d57
test(state): guard compact_rows threading through batched tip-row fetch
kshitijk4poor Aug 3, 2026
7db2827
refactor(state): chunk the batched tip-row IN clause at 900 ids
kshitijk4poor Aug 3, 2026
b651121
fix(yuanbao): clear _processing_msg_ids/_processing_msg_texts after e…
AhmetArif0 May 10, 2026
81c8645
fix(yuanbao): evict stale entries from _member_cache on TTL expiry
AhmetArif0 May 10, 2026
f3add02
fix(yuanbao): pop tracking entries only for truthy matching msg_id + …
kshitijk4poor Aug 3, 2026
4cf2fb5
chore: add frizikk to AUTHOR_MAP
kshitijk4poor Aug 3, 2026
2b0d58e
Merge pull request #77636 from kshitijk4poor/chore/attrib-frizikk
kshitijk4poor Aug 3, 2026
9891f4b
perf(zai): parallelize endpoint detection probes
light-merlin-dark Apr 11, 2026
9e99a33
test(zai): cover parallel-probe contracts + restore candidate-model loop
kshitijk4poor Aug 3, 2026
0e4daad
perf(zai): early-exit when the highest-priority endpoint wins (simpli…
kshitijk4poor Aug 3, 2026
94ef36a
Bound MiniMax OAuth error responses
ooiuuii Jul 1, 2026
219bb35
fix(minimax-oauth): read streamed error bodies inside the client cont…
kshitijk4poor Aug 3, 2026
a762625
chore: map xaydinoktay@gmail.com to aydnOktay
kshitijk4poor Aug 3, 2026
ebda995
Merge pull request #77640 from kshitijk4poor/chore/attrib-aydnoktay
kshitijk4poor Aug 3, 2026
f327c89
chore(contributors): map four B2 salvage author emails (#77641)
kshitijk4poor Aug 3, 2026
ffb5430
perf(session-search): project fields before enrichment
frizikk Jul 14, 2026
f795d54
test(session-search): guard projected enrichment
frizikk Jul 14, 2026
2f14c3e
fix: skip memory prefetch on trivial user prompts (greetings)
ayushere Aug 3, 2026
46073d7
chore: add ayushere to AUTHOR_MAP
ayushere May 15, 2026
c093492
refactor(memory): single shared trivial-prompt classifier + gate tests
kshitijk4poor Aug 3, 2026
34d6095
refactor(honcho): delegate _is_trivial_prompt wholly to the shared cl…
kshitijk4poor Aug 3, 2026
b9b0505
fix(desktop): measure adaptive stream flush through the deferred comm…
Adolanium Jul 27, 2026
3060bee
fix(desktop): dedupe optimistic user turns for all wire references, n…
pnascimento9596 Jul 31, 2026
fed10fa
test(desktop): cover wire reference normalization edges
pnascimento9596 Jul 31, 2026
2ebe175
fix(desktop): sort reference-kinds import per lint gate
pnascimento9596 Aug 1, 2026
ed66ff1
fix(desktop): full-jitter backoff on gateway WS reconnect loops
Aug 1, 2026
5ffbea8
fix(desktop): escalate gateway reconnect on elapsed time, not attempt…
kshitijk4poor Aug 3, 2026
94fac06
chore(contributors): map vittoria3103.123@gmail.com -> VittoriaLanzo …
kshitijk4poor Aug 3, 2026
1306ac0
fix(desktop): un-break the .btn-arc rule — '*/' inside a CSS comment …
rerdi92 Aug 3, 2026
82fd574
perf(desktop): stop idle chat re-renders — memo ChatView, stable tile…
hdd69 Aug 3, 2026
bc3fa54
perf(curator): trim dead tool-schema from the LLM review fork
VittoriaLanzo Jul 10, 2026
b6f15f5
fix(desktop): keep a mid-turn reply on screen when its session is reo…
OutThisLife Aug 3, 2026
34f0427
fix(desktop): stop a finished reply rendering twice after history cat…
OutThisLife Aug 3, 2026
cb11a7e
chore(contributors): map two B3 salvage author emails (#77685)
kshitijk4poor Aug 3, 2026
1f692a8
refactor(desktop): hoist the reference-line matcher; drop dead textWi…
kshitijk4poor Aug 3, 2026
53398ff
fix(desktop): do not sandwich structured mid-turn rows with inflight …
686f6c61 Aug 2, 2026
2ecc1db
fix(desktop): scope inflight dump suppression to the live turn tail
686f6c61 Aug 2, 2026
dbefd27
fix(desktop): require structure-bearing row for live-tail same-turn c…
686f6c61 Aug 3, 2026
7a4d047
refactor(desktop): one live-tail vocabulary for transcript reconcilia…
OutThisLife Aug 3, 2026
0de8c32
fix(dashboard): cache plugins hub payload and avoid auth probes
ahmadashfq Jul 15, 2026
9fa17c1
test(dashboard): cover install-hook invalidation of plugins hub cache
kshitijk4poor Aug 3, 2026
1f1acc0
fix(dashboard): warm cold check_fn verdicts with a background probe
kshitijk4poor Aug 3, 2026
c4ac62a
fix(desktop): cancel the pending commit-cost measurement rAF
kshitijk4poor Aug 3, 2026
c163932
perf(dashboard): skip full InsightsEngine on /api/analytics/usage (#1…
rodboev Jun 4, 2026
e3ce092
perf(dashboard): keep tools in focused analytics usage (#18511)
rodboev Jul 14, 2026
773d690
refactor(insights): drop consumer-less get_skill_breakdown alias (sim…
kshitijk4poor Aug 3, 2026
b536f06
Merge pull request #77644 from NousResearch/bb/review-75967
OutThisLife Aug 3, 2026
105aba6
fix(web): clamp dashboard pagination and analytics-days params (#3920…
aydnOktay Aug 3, 2026
2000278
fix(clamps): raise profile fan-out limit to le=500 (simplify-pass fin…
kshitijk4poor Aug 3, 2026
9645ea8
fix(web): avoid blocking provider validation
youzi-1122 Jul 20, 2026
050b953
perf(plugins): seed plugin routes from sessionStorage cache for insta…
AllardQuek May 4, 2026
057939c
fix(plugins): validate cached manifests are an array
AllardQuek Jul 19, 2026
41bba3c
test(plugins): export cache helpers and add focused fallback/refresh …
AllardQuek Jul 19, 2026
c0c45ab
fix(plugins): keep loading gate when cached manifests include a /chat…
kshitijk4poor Aug 3, 2026
decf12e
perf(dashboard): serve hashed /assets bundles with immutable cache he…
kshitijk4poor Aug 3, 2026
6611d87
feat: reset-aware primary restore — stay on fallback until the rate-l…
WojtekMR3 Jul 19, 2026
4c2d473
fix(credential_pool): run next_available_at under the pool lock
kshitijk4poor Aug 3, 2026
9cd605f
fix(credential_pool): defer single-use-token refresh outside threadin…
necoweb3 Jul 26, 2026
687dd63
fix(credential_pool): serialize deferred-refresh pool mutations
kshitijk4poor Aug 3, 2026
2f32092
`hermes sessions optimize-storage` aborts with
Kyzcreig Jul 26, 2026
c2c95f5
Merge pull request #77713 from NousResearch/bb/review-76744-fold
OutThisLife Aug 3, 2026
48e12a0
perf(tools): shrink lazy tool catalog overhead
carbongotfound Jul 29, 2026
13172fc
refactor(tool-search): drop dead fallback ladder in _available_source…
kshitijk4poor Aug 3, 2026
82019e7
fix(credential_pool): unpack the tuple in next_available_at's gate
kshitijk4poor Aug 3, 2026
8167b9f
chore: map copii.list@gmail.com to stremtec
kshitijk4poor Aug 3, 2026
4a99ba1
Merge pull request #77760 from kshitijk4poor/chore/attrib-stremtec
kshitijk4poor Aug 3, 2026
dae5df2
chore(contributors): map marzukia@users.noreply.github.com -> marzuki…
kshitijk4poor Aug 3, 2026
f1133c9
chore: map bot@bkstock.dev to BKStock
kshitijk4poor Aug 3, 2026
7e47a0f
Merge pull request #77779 from kshitijk4poor/chore/attrib-bkstock
kshitijk4poor Aug 3, 2026
eaf4d51
perf(session): route SQLite PRAGMAs through central apply_database_pr…
crayfish-ai Jul 30, 2026
3ac7168
fix(pr): remove remnant local PRAGMAs from PR branch
crayfish-ai Jul 30, 2026
7026177
test(session): guard config-gated performance PRAGMAs across all conn…
kshitijk4poor Aug 3, 2026
416b56b
fix(desktop): flush queued deltas on window focus
unixwzrd Jul 30, 2026
7700597
perf(desktop): stop scroll and status loops in busy sessions
myk0la-b Jul 30, 2026
52fb96d
perf(desktop): pause hidden-pane timers in agents view, cron sidebar,…
hariNEzuMI928 Aug 3, 2026
e2a2149
style(desktop): restore alphabetical import order in agents/index.tsx
kshitijk4poor Aug 3, 2026
aece98c
refactor(desktop): shared pulse beat + fully-gated cron peek (simplif…
kshitijk4poor Aug 3, 2026
9d76d48
fix(lint): import sort + eslint-disable for timer-handle ref clear in…
kshitijk4poor Aug 3, 2026
dab7c88
fix(state): narrow FTS UPDATE triggers with AFTER UPDATE OF + migration
Jul 31, 2026
a5ce909
fix(state): fail closed on CJK trigger migration
yuzilongleif-collab Aug 1, 2026
b66111f
fix(state): quarantine CJK when ensure soft-fails after OF migration
Aug 1, 2026
ae17163
refactor(state): drop unreachable regex guard in trigger migration
kshitijk4poor Aug 3, 2026
c7fd21a
fix(security): reject always-blocked OpenViking endpoints
zapabob Jul 23, 2026
65bcca6
fix(openviking): fail closed on blocked endpoints
zapabob Aug 2, 2026
a3f6953
fix(openviking): don't spawn a second server onto a live port
jeff-mettel Jul 30, 2026
9014aa0
fix(openviking): drop stale "disabled for this Hermes run" warnings
Jul 26, 2026
f0cb219
fix(openviking): re-arm the commit guard after in-place compression
jeff-mettel Jul 30, 2026
f94914f
test(openviking): cover the compression lifecycle, not a hand-set latch
jeff-mettel Jul 30, 2026
5396dd8
fix(memory): read non-secret provider config from config.yaml for Ope…
PRATHAMESH75 Jul 20, 2026
4ebe990
fix(openviking): read recall settings from config.yaml first, env var…
justemu Jul 11, 2026
4b57943
test(openviking): cover config.yaml recall settings with temp-HERMES_…
justemu Jul 11, 2026
e43bc0b
fix(openviking): integrate reliability and configuration hardening
ehz0ah Aug 2, 2026
8fb9c3b
chore(contributors): map OpenViking source authors
ehz0ah Aug 2, 2026
e443d32
test(retaindb): guard scoped secret config resolution
ehz0ah Aug 3, 2026
a49a9e5
fix(openviking): verify servers before sending credentials
ehz0ah Aug 3, 2026
41cc4a1
fix(openviking): catch endpoint errors in setup validation functions
kshitijk4poor Aug 3, 2026
7d066c3
fix(state): deduplicate session system prompts
embwl0x Aug 2, 2026
26133b5
chore: map cicav legacy noreply email
kshitijk4poor Aug 3, 2026
5828687
fix(tui): avoid writable Kanban opens on empty polls
joncaldwell90 Jul 31, 2026
fe4cf36
Merge pull request #77796 from kshitijk4poor/chore/attrib-cicav
kshitijk4poor Aug 3, 2026
5bbd0db
fix(context): dedupe subdirectory hints by content digest and skip ba…
Jul 27, 2026
06ae5b6
perf(state): batch the turn flush into one SQLite transaction
Aug 3, 2026
b58b3ad
perf(tui-gateway): batch branch-seed history copies (whole-bug-class)
kshitijk4poor Aug 3, 2026
84146fb
test(run-agent): update flush-path fakes and assertions for batched w…
kshitijk4poor Aug 3, 2026
da6d960
refactor(state): fold simplify findings — reuse _insert_message_rows,…
kshitijk4poor Aug 3, 2026
5bff398
fix(tests): update two more append_message.call_args assertions to ap…
kshitijk4poor Aug 3, 2026
128ca2e
perf(tui): memoize useSessionLifecycle return (idea from #38491)
stremtec Aug 3, 2026
1770028
ci: retry uv python install
ethernet8023 Aug 3, 2026
67d4bbb
fix(state): route session-resume reads through the WAL read-only conn…
pierrenode Jul 28, 2026
d48a78a
chore: add contributor email mapping for ArcherQAQ
kshitijk4poor Aug 3, 2026
d5584a3
Merge pull request #77821 from kshitijk4poor/chore-archer-email
kshitijk4poor Aug 3, 2026
fc32a38
fix(model_metadata): rewrite localhost->IPv4 for the remaining local …
pierrenode Aug 3, 2026
733e7d2
fix(model_metadata): guard _localhost_to_ipv4 against non-string urls
kshitijk4poor Aug 3, 2026
25a9c2c
perf(cold-start): mitigate ~14s GIL stall during backend init (#60800)
jinglun010-cpu Jul 8, 2026
0bb1462
test: harden cold-start regression tests + debug-log the env-var skip
kshitijk4poor Aug 3, 2026
003b4c8
perf(gateway): per-platform skip_context_files to cut agent build lat…
ArcherQAQ Aug 3, 2026
2f09df5
fix(relay): route Discord tool-progress into the auto-thread, not the…
benbarclay Aug 3, 2026
1f8acb3
fix(agent): stop re-probing endpoints that blackhole TCP connects
rlaope Aug 3, 2026
3c27eb6
chore: release v0.20.0 (2026.8.3)
teknium1 Aug 3, 2026
3763706
chore: add contributor email mapping for Ahmett101
kshitijk4poor Aug 3, 2026
f03eb25
Merge pull request #77867 from kshitijk4poor/chore-ahmett-email
kshitijk4poor Aug 3, 2026
d1c6c6b
perf(moa): cache resolved preset + per-slot runtime to cut cold-start…
Jul 18, 2026
e6f1d61
fix(discord): leave voice channels before cancelling the bot task
BrinShadewater Aug 1, 2026
c0b0cc3
feat(image): parallelize image_generate batches
EndeavorYen May 28, 2026
952d86b
fix(file-sync): serialize concurrent sync cycles
EndeavorYen Jul 13, 2026
a7ad713
fix(tool-executor): unpack 5-tuple runnable_calls in _max_workers_for…
kshitijk4poor Aug 3, 2026
9267c78
fix: exponential backoff for rate-limit fallback cooldown
May 22, 2026
df9dbba
fix(backoff): keep 60s first-hit cooldown, escalate only on consecuti…
kshitijk4poor Aug 3, 2026
861ca18
fix(catalog): wire api_key auth headers for http MCP servers
JonthanaHanh Aug 3, 2026
f8f4755
perf(compressor): release allocator pages after successful compaction
kshitijk4poor Aug 3, 2026
00475e1
fix(catalog): validate http+api_key manifests declare the header's en…
kshitijk4poor Aug 3, 2026
23f8ae3
fix(agent): cap auxiliary LLM concurrency per task
konsisumer Jul 18, 2026
ddae511
fix: thread extra_headers through the call_llm split
kshitijk4poor Aug 3, 2026
7eefb09
fix(nix): tie devShell's HERMES_PYTHON to the venv actually on PATH
ethernet8023 Aug 3, 2026
9b9cbdd
fix(system_prompt): move skills index to the volatile band
marzukia Jun 2, 2026
9555525
docs(system_prompt): fix stale reconstruct_static_prefix docstring ex…
kshitijk4poor Aug 3, 2026
efbfe08
fix(prompt_size): search volatile tier for skills block after the sta…
kshitijk4poor Aug 3, 2026
164c3d6
chore: add contributor email mapping for zabih-sudo
kshitijk4poor Aug 3, 2026
d92a28d
Merge pull request #77902 from kshitijk4poor/chore-zabih-email
kshitijk4poor Aug 3, 2026
f40d63d
chore: add contributor email mapping for HAOWANG116
kshitijk4poor Aug 3, 2026
53cc628
Merge pull request #77912 from kshitijk4poor/chore-haowang-email
kshitijk4poor Aug 3, 2026
aad8f74
fix(backup): serialize and atomically publish snapshots
Jul 22, 2026
827bb0d
chore: add contributor email mapping for ElSnacko
kshitijk4poor Aug 3, 2026
0845232
fix: prefer explicit anthropic api key
kshitijk4poor Aug 3, 2026
a991dfc
docs: document /personality none|default|neutral reset across persona…
teknium1 Aug 3, 2026
9a9b670
fix(relay): avoid concurrent turn scope corruption
bbednarski9 Jul 30, 2026
704baa5
fix(relay): preserve legacy turn shims
bbednarski9 Jul 30, 2026
a2a08fe
fix(relay): gate skipped turn metrics
bbednarski9 Jul 30, 2026
2e65b0c
test(relay): enforce LIFO in overlap regression
bbednarski9 Jul 31, 2026
e1caa61
fix(relay): preserve skipped turn context
bbednarski9 Aug 3, 2026
dac4bbe
fix comment about relay workaround
jquesnelle Aug 3, 2026
3c3ae74
feat(models): add qwen3.8-max to Nous portal + OpenRouter catalogs, r…
teknium1 Aug 3, 2026
91937a6
test: swap context-switch-guard fixture off qwen3.8-max-preview
teknium1 Aug 3, 2026
70db671
Merge pull request #76643 from NousResearch/fix/termux-nemo-relay
jquesnelle Aug 4, 2026
d218496
Merge pull request #77924 from kshitijk4poor/chore/author-map-elsnacko
kshitijk4poor Aug 4, 2026
3a0a295
fix(agent): keep context_length pin for named custom providers
xxxigm Aug 3, 2026
942ff91
test(gateway): cover named-custom context pin on session-info banner
xxxigm Aug 3, 2026
60c721a
fix(model_metadata): read llama.cpp context from meta.n_ctx + accept …
ElSnacko Jul 15, 2026
f66d625
chore: add contributor email mapping for johnrazmus
kshitijk4poor Aug 4, 2026
e4888a9
Merge pull request #78226 from kshitijk4poor/chore-razmus-email
kshitijk4poor Aug 4, 2026
3b0bb3b
fix(gateway): keep event loop alive during /compress and Relay drain
Dannyzen Aug 4, 2026
9c88625
fix(gateway): bound go_dormant ws.close with teardown timeout
kshitijk4poor Aug 4, 2026
e623432
fix: close the Codex app-server session on agent teardown
johnrazmus Aug 4, 2026
ba9068c
fix(agent): discard bare tool-call marker before fallback/persistence…
JoaoMarcos44 Aug 4, 2026
70d7e4c
fix(agent): repair sessions already contaminated with stale tool-call…
JoaoMarcos44 Aug 4, 2026
e1a2739
feat(cli): add sessions clean-markers to permanently purge stale tool…
JoaoMarcos44 Aug 4, 2026
e18c040
fix(cli): back up state.db before clean-markers writes by default
JoaoMarcos44 Aug 4, 2026
15d51bb
refactor: dedup stale-marker regex — use compiled _STALE_MARKER_RE in…
kshitijk4poor Aug 4, 2026
9938d20
fix(conversation_loop): compress messages on output-cap retry path (#…
Rafa-Ross Aug 3, 2026
04098e2
fix(conversation_loop): prune dead vision-strip fallback; harden outp…
BobClawblaw Aug 3, 2026
78c8bcd
chore: drop CHANGELOG.md and docs/reports/ — not shipped with salvage…
kshitijk4poor Aug 4, 2026
9fc8926
perf: reuse request_input_estimate instead of recomputing estimate_re…
kshitijk4poor Aug 4, 2026
743dc94
chore: AUTHOR_MAP — add BobClawblaw for PR #77870 salvage
kshitijk4poor Aug 4, 2026
9ce917f
chore(ci): rerun checks
bbednarski9 Aug 4, 2026
bdc82e3
perf(gateway): prewarm /model picker cache on TUI startup
ZachariahChu Jul 26, 2026
5c078b9
test(tui): pin picker-cache prewarm wiring in entry.main()
kshitijk4poor Aug 4, 2026
dbafb59
perf(cli): check local auth.json/config before slow provider registry…
frohsinnllc Jul 12, 2026
8f52040
test(cli): regression tests pinning auth-first ordering skips registr…
kshitijk4poor Aug 4, 2026
9a20d7f
perf(desktop): keep spinner frames out of React commits
myk0la-b Jul 29, 2026
be54f28
test(desktop): cover minimized/hidden window-state + visibilitychange…
kshitijk4poor Aug 4, 2026
9076ada
fmt(js): `npm run fix` on merge (#78271)
hermes-seaeye[bot] Aug 4, 2026
7098862
perf(gateway): replace SSE poll loop with call_soon_threadsafe-fed as…
zabih-sudo Jul 15, 2026
7a1f2e3
refactor(gateway): extract _sse_frame() helper, dedup 5 inline SSE en…
zabih-sudo Jul 15, 2026
1a09b07
refactor(gateway): route all three SSE writers through _sse_frame()
zabih-sudo Jul 16, 2026
221afc0
refactor(gateway): route session event stream through _sse_frame (ens…
zabih-sudo Jul 16, 2026
fc8e393
test: add cross-thread put_threadsafe + long-reasoning tail tests
zabih-sudo Jul 31, 2026
98165da
fix: reconstruct fused test after conflict resolution
kshitijk4poor Aug 3, 2026
64882bc
perf(tui): bound reasoning-clean input to the displayed tail
zabih-sudo Aug 4, 2026
7e344dc
test: exercise the production _loop_ref path in put_threadsafe tests
kshitijk4poor Aug 4, 2026
fb4e17b
fix(test): feed the SSE writers an asyncio queue, not queue.Queue
kshitijk4poor Aug 4, 2026
db0bd42
fix(credential-pool): re-select in acquire_lease after a deferred ref…
kshitijk4poor Aug 4, 2026
4075c8f
fix(credential-pool): lock the quarantine read-modify-write of _entries
kshitijk4poor Aug 4, 2026
cb3e8e9
perf(file-ops): eliminate redundant subprocess calls in write_file an…
zzy-git Jun 30, 2026
eb78ab2
fix(file-ops): decouple BOM detection from pre_content, add V4A backw…
zzy-git Jul 15, 2026
fcae5ad
fix(file-ops): surrogatepass in bytes_written encode (review finding)
kshitijk4poor Aug 3, 2026
8c19e29
refactor(file-ops): fold simplify-pass findings
kshitijk4poor Aug 3, 2026
622b9a9
chore: update uv.lock for tomli dependency (rebase fix)
kshitijk4poor Aug 3, 2026
1709f82
chore: remove dead tomli dependency declaration
kshitijk4poor Aug 4, 2026
e05eba2
fix(telegram+sqlite): resolve polling conflict loop + misleading WAL …
kshitijk4poor Aug 4, 2026
e9a8b70
chore: map jun@junho.co to junhohong
kshitijk4poor Aug 4, 2026
3aeff23
Merge pull request #78362 from kshitijk4poor/chore/attrib-junhohong
kshitijk4poor Aug 4, 2026
cab8673
fix(dashboard): reload loopback tabs after stale session-token closes
izumi0uu Jun 28, 2026
19e697d
fix: update ChatPage test import for react-router v7
kshitijk4poor Aug 4, 2026
b8b17b8
fix: wire HermesConsoleModal WS into stale-token reload guard
kshitijk4poor Aug 4, 2026
d2772b4
fix(xai): honor configured web search backend on Responses path
xxxigm Aug 4, 2026
29eba9c
test(xai): cover Firecrawl vs native web_search on Responses
xxxigm Aug 4, 2026
f5be923
refactor(xai): simplify _xai_prefers_native_web_search to use registry
kshitijk4poor Aug 4, 2026
80c7ccf
fix(relay): gate skipped task completion
bbednarski9 Aug 4, 2026
f663190
fix(model-switch): treat models dict as metadata, not allowlist
HexLab98 Jul 27, 2026
e6977f4
test(model-switch): cover Ollama context_length models dict probing
HexLab98 Jul 27, 2026
42708f8
Merge pull request #74864 from bbednarski9/fix/relay-concurrent-turn-…
jquesnelle Aug 4, 2026
5943bab
Merge branch 'main' into feat/hermes-relay-model-metrics
jquesnelle Aug 4, 2026
3fa318a
Merge pull request #68881 from afourniernv/feat/hermes-relay-model-me…
jquesnelle Aug 4, 2026
daf67f2
Merge branch 'main' into feat/hermes-relay-tool-metrics
jquesnelle Aug 4, 2026
91337e5
fix(desktop): ⌘1 / ⌃Tab return to the chat from a full-page view
OutThisLife Aug 4, 2026
5d24594
feat(desktop): expose native OS notifications to plugins via ctx.noti…
serefyarar Aug 2, 2026
1ed702b
refactor(desktop): share render weight between the two transcript bud…
OutThisLife Aug 4, 2026
a538b1c
fix(desktop): bound the transcript reaching assistant-ui by render co…
OutThisLife Aug 4, 2026
62012a5
feat(desktop): Show earlier pages the DOM, then pulls older history f…
OutThisLife Aug 4, 2026
e8ccb4a
feat(desktop): ctx.os — the curated OS door for plugins
OutThisLife Aug 4, 2026
fe859a1
fix(credential-pool): clear exhaustion state on key rotation (#22622)
pasevin Aug 4, 2026
be55e99
Merge pull request #78679 from NousResearch/bb/tab-keys-leave-pages
OutThisLife Aug 4, 2026
6acae30
feat(desktop): ctx.os — plugins get a curated door to native notifica…
OutThisLife Aug 4, 2026
2d70f56
fix(agent): adopt .env credential/base-url edits at the turn boundary…
rapsealk Aug 4, 2026
97641a8
fix(debug): say where a client-side log lives instead of "(file not f…
OutThisLife Aug 4, 2026
b281b2c
Merge pull request #78683 from NousResearch/bb/55191-transcript-window
OutThisLife Aug 4, 2026
dcd7504
fix(credential-pool): short cooldown for sole credential on transient…
ahmadalzaro1 Jun 26, 2026
d1eb08f
fix: thread sole_credential into next_available_at sibling site
kshitijk4poor Aug 4, 2026
9cd0338
fix(credential-pool): bench a billing 403 fully, even as the sole key
OutThisLife Aug 4, 2026
9712b8f
test: teach the hand-rolled fake pools the failure_reason kwarg
OutThisLife Aug 4, 2026
d119675
feat(profiles): REST export/import + extra_files overlay hook
OutThisLife Aug 4, 2026
bde8c4e
feat(cli): /export and /import slash commands for profile sharing
OutThisLife Aug 4, 2026
6e7eafc
feat(desktop): share a profile as a portable bundle - theme, layout, …
OutThisLife Aug 4, 2026
b3e45a3
Discord drops an empty outbound message instead of sending it (#78815)
OutThisLife Aug 4, 2026
ec0c8d9
feat(state): sessions carry read/unread state
OutThisLife Aug 4, 2026
f40fbcf
Merge pull request #68882 from afourniernv/feat/hermes-relay-tool-met…
jquesnelle Aug 4, 2026
cc8e974
feat(desktop): the layout tree moves a tab block as one unit
OutThisLife Aug 4, 2026
33c1d1f
feat(desktop): shift-click and opt-click select tabs to drag together
OutThisLife Aug 4, 2026
d98287f
Merge pull request #78831 from NousResearch/bb/session-read-state
OutThisLife Aug 4, 2026
28b3b0d
feat(gateway): session.workspace.move — re-home a stored session's wo…
OutThisLife Aug 4, 2026
edae3ee
feat(desktop): move a session to another project from its row menu
OutThisLife Aug 4, 2026
1d6606d
fix(profiles): exported archives open in Finder (GNU tar, not PAX)
OutThisLife Aug 4, 2026
0106844
Merge pull request #78849 from NousResearch/bb/multi-tab-drag
OutThisLife Aug 4, 2026
ec9572f
Merge pull request #78854 from NousResearch/bb/session-move-project
OutThisLife Aug 4, 2026
0a48af2
Merge pull request #78812 from NousResearch/bb/profile-share
OutThisLife Aug 4, 2026
4371712
fix(models): a model id missing its vendor prefix says so instead of …
OutThisLife Aug 4, 2026
e926e4c
Bridge protected patch tree to Nous upstream baseline b3e45a3d46ce1af…
vectorcmd Aug 4, 2026
4306de4
Merge remote-tracking branch 'upstream/main' into ace/patches-nightly
vectorcmd Aug 4, 2026
313fb1f
chore: record Nous upstream baseline 43717123ca15
vectorcmd Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ace/upstream-main.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b3e45a3d46ce1af52a267cc3aaa3cb6c4f52d1e8
43717123ca1566a073270c5a61431e2e0e4a0211
22 changes: 22 additions & 0 deletions agent/conversation_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -4243,6 +4243,28 @@ def _perform_api_call(next_api_kwargs):
f" Check which providers support tools: https://openrouter.ai/models/{_model}"
)

# Actionable hint for a bare 404 on a provider whose catalogue
# uses ``vendor/model`` ids. A model id that lost its prefix
# (e.g. ``nemotron-…`` instead of ``nvidia/nemotron-…``) gets
# a content-free "404 page not found" from the provider that
# never names the model, so it reads like an outage or an auth
# failure. Name the real cause and the exact id to use (#78796).
if getattr(api_error, "status_code", None) == 404:
try:
from hermes_cli.model_normalize import suggest_prefixed_model_id

_suggestion = suggest_prefixed_model_id(_provider, _model)
except Exception:
_suggestion = None
if _suggestion:
agent._buffer_vprint(
f" 💡 Model '{_model}' is not a valid id for provider {_provider} — "
f"it is missing its vendor prefix."
)
agent._buffer_vprint(
f" Did you mean '{_suggestion}'? Re-pick it with `hermes model`."
)

# Check for interrupt before deciding to retry
if agent._interrupt_requested:
# Preserve a pending redirect (mid-stream correction): the
Expand Down
38 changes: 38 additions & 0 deletions agent/error_classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,32 @@ def is_auth(self) -> bool:
"no endpoints found that support tool use",
]


def _model_id_missing_known_prefix(model: str, provider: str) -> bool:
"""True when a bare model id is only known to the provider as ``vendor/id``.

Some providers answer a malformed model id with a naked 404 that names
nothing — NVIDIA NIM returns ``404 page not found`` for a bare
``nemotron-3-ultra-550b-a55b``, indistinguishable from a bad endpoint
path. Consulting the curated catalogue tells the two apart: if the id
carries no ``/`` but the catalogue has exactly one entry ending in
``/<id>``, the prefix was dropped and the failure is deterministic.

Never guesses — an id absent from the catalogue (a local NIM container,
a proxied model) returns False so genuine endpoint problems keep their
retryable ``unknown`` classification.
"""
name = (model or "").strip()
if not name or "/" in name:
return False
try:
from hermes_cli.model_normalize import suggest_prefixed_model_id

return bool(suggest_prefixed_model_id((provider or "").strip(), name))
except Exception:
return False


# Malformed-message-array 400s. Deterministic request-shape rejections that
# describe the *transcript* being invalid, not a parameter. The canonical
# case: a stream dies mid-response and Hermes persists a content-less
Expand Down Expand Up @@ -1061,6 +1087,18 @@ def _classify_by_status(
retryable=False,
should_fallback=True,
)
# A bare id that the provider's catalogue only knows in prefixed form
# is a malformed model id, not a routing glitch — NVIDIA NIM answers
# one with a naked ``404 page not found`` that names nothing, so the
# generic branch below burns three retries and reports what looks
# like an outage (#78796). Deterministic: don't retry, and let the
# model_not_found surface carry the real cause.
if _model_id_missing_known_prefix(model, provider):
return result_fn(
FailoverReason.model_not_found,
retryable=False,
should_fallback=True,
)
# Generic 404 with no "model not found" signal — could be a wrong
# endpoint path (common with local llama.cpp / Ollama / vLLM when
# the URL is slightly misconfigured), a proxy routing glitch, or
Expand Down
101 changes: 89 additions & 12 deletions agent/tool_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ class _ManagedToolResult:
args: dict[str, Any]
middleware_trace: list[dict[str, Any]]
blocked: bool
dispatched: bool


class _ConcurrentToolAuthorizationGate:
Expand Down Expand Up @@ -383,12 +384,13 @@ def excluded_seconds(self) -> float:

def _managed_values(
outcome: _ManagedToolResult,
) -> tuple[Any, dict[str, Any], list[dict[str, Any]], bool]:
) -> tuple[Any, dict[str, Any], list[dict[str, Any]], bool, bool]:
return (
outcome.result,
outcome.args,
outcome.middleware_trace,
outcome.blocked,
outcome.dispatched,
)


Expand Down Expand Up @@ -571,6 +573,7 @@ def _hermes_pipeline(relay_args: dict[str, Any]) -> Any:
args=state["args"],
middleware_trace=state["middleware_trace"],
blocked=bool(state["blocked"]),
dispatched=bool(state["dispatched"]),
)


Expand Down Expand Up @@ -688,12 +691,27 @@ def execute_tool_calls_concurrent(agent, assistant_message, messages: list, effe
if agent._interrupt_requested:
print(f"{agent.log_prefix}⚡ Interrupt: skipping {num_tools} tool call(s)")
for tc in tool_calls:
cancelled_result = (
f"[Tool execution cancelled — {tc.function.name} was skipped "
"due to user interrupt]"
)
messages.append(make_tool_result_message(
tc.function.name,
f"[Tool execution cancelled — {tc.function.name} was skipped due to user interrupt]",
cancelled_result,
tc.id,
effect_disposition="none",
))
_emit_terminal_post_tool_call(
agent,
function_name=tc.function.name,
function_args={},
result=cancelled_result,
effective_task_id=effective_task_id,
tool_call_id=getattr(tc, "id", "") or "",
status="cancelled",
error_type="user_interrupt",
error_message="Tool execution skipped due to user interrupt",
)
_flush_session_db_after_tool_progress(
agent,
messages,
Expand Down Expand Up @@ -840,6 +858,7 @@ def _run_tool(
# submit site below (GHSA-qg5c-hvr5-hjgr, #13617).
start = time.time()
blocked = False
dispatched = False
start_advanced = False

def _advance_start(callback=None) -> None:
Expand Down Expand Up @@ -883,6 +902,7 @@ def _execute(next_args: dict[str, Any]) -> Any:
function_args = managed.args
middleware_trace = managed.middleware_trace
blocked = managed.blocked
dispatched = managed.dispatched
except KeyboardInterrupt:
try:
agent.interrupt("keyboard interrupt")
Expand Down Expand Up @@ -913,6 +933,17 @@ def _execute(next_args: dict[str, Any]) -> Any:
result = f"Error executing tool '{function_name}': {tool_error}"
logger.error("_invoke_tool raised for %s: %s", function_name, tool_error, exc_info=True)
duration = time.time() - start
if not blocked and not dispatched:
_emit_terminal_post_tool_call(
agent,
function_name=function_name,
function_args=function_args,
result=result,
effective_task_id=effective_task_id,
tool_call_id=getattr(tool_call, "id", "") or "",
duration_ms=int(duration * 1000),
middleware_trace=list(middleware_trace),
)
is_error, _ = _detect_tool_failure(function_name, result)
if is_error:
logger.info("tool %s failed (%.2fs): %s", function_name, duration, result[:200])
Expand Down Expand Up @@ -1162,6 +1193,7 @@ def _execute(next_args: dict[str, Any]) -> Any:
result=function_result,
effective_task_id=effective_task_id,
tool_call_id=getattr(tc, "id", "") or "",
duration_ms=int((timeout_s or 0.0) * 1000),
status="timeout",
error_type="tool_timeout",
error_message=function_result,
Expand Down Expand Up @@ -1204,6 +1236,19 @@ def _execute(next_args: dict[str, Any]) -> Any:
name = function_name
args = function_args
progress_function_name = function_name
if _parse_error is not None:
_emit_terminal_post_tool_call(
agent,
function_name=function_name,
function_args=function_args,
result=function_result,
effective_task_id=effective_task_id,
tool_call_id=getattr(tc, "id", "") or "",
status="error",
error_type="invalid_tool_arguments",
error_message="Tool arguments must be a valid JSON object",
middleware_trace=list(middleware_trace),
)
if blocked:
effect_disposition = "none"

Expand Down Expand Up @@ -1394,12 +1439,27 @@ def execute_tool_calls_sequential(agent, assistant_message, messages: list, effe
agent._vprint(f"{agent.log_prefix}⚡ Interrupt: skipping {len(remaining_calls)} tool call(s)", force=True)
for skipped_tc in remaining_calls:
skipped_name = skipped_tc.function.name
cancelled_result = (
f"[Tool execution cancelled — {skipped_name} was skipped "
"due to user interrupt]"
)
messages.append(make_tool_result_message(
skipped_name,
f"[Tool execution cancelled — {skipped_name} was skipped due to user interrupt]",
cancelled_result,
skipped_tc.id,
effect_disposition="none",
))
_emit_terminal_post_tool_call(
agent,
function_name=skipped_name,
function_args={},
result=cancelled_result,
effective_task_id=effective_task_id,
tool_call_id=getattr(skipped_tc, "id", "") or "",
status="cancelled",
error_type="user_interrupt",
error_message="Tool execution skipped due to user interrupt",
)
if not _flush_session_db_after_tool_progress(
agent,
messages,
Expand All @@ -1414,6 +1474,17 @@ def execute_tool_calls_sequential(agent, assistant_message, messages: list, effe
tool_call.function.arguments
)
if malformed_args_result is not None:
_emit_terminal_post_tool_call(
agent,
function_name=function_name,
function_args=function_args,
result=malformed_args_result,
effective_task_id=effective_task_id,
tool_call_id=getattr(tool_call, "id", "") or "",
status="error",
error_type="invalid_tool_arguments",
error_message="Tool arguments must be a valid JSON object",
)
messages.append(
make_tool_result_message(
function_name,
Expand Down Expand Up @@ -1468,6 +1539,7 @@ def execute_tool_calls_sequential(agent, assistant_message, messages: list, effe

middleware_trace: list[dict[str, Any]] = []
_execution_blocked = False
_execution_dispatched = False

tool_start_time = time.time()

Expand All @@ -1479,7 +1551,7 @@ def _execute(next_args: dict) -> Any:
merge=next_args.get("merge", False),
store=agent._todo_store,
)
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand Down Expand Up @@ -1510,7 +1582,7 @@ def _execute(next_args: dict) -> Any:
db=session_db,
current_session_id=agent.session_id,
)
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand Down Expand Up @@ -1549,7 +1621,7 @@ def _execute(next_args: dict) -> Any:
),
)
return result
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand All @@ -1571,7 +1643,7 @@ def _execute(next_args: dict) -> Any:
multi_select=next_args.get("multi_select", False),
callback=agent.clarify_callback,
)
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand All @@ -1592,7 +1664,7 @@ def _execute(next_args: dict) -> Any:
count=next_args.get("count"),
callback=getattr(agent, "read_terminal_callback", None),
)
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand Down Expand Up @@ -1626,7 +1698,7 @@ def _execute(next_args: dict) -> Any:
try:
def _execute(next_args: dict) -> Any:
return agent._dispatch_delegate_task(next_args)
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand Down Expand Up @@ -1659,7 +1731,7 @@ def _execute(next_args: dict) -> Any:
try:
def _execute(next_args: dict) -> Any:
return agent.context_compressor.handle_tool_call(function_name, next_args, messages=messages)
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand Down Expand Up @@ -1695,7 +1767,7 @@ def _execute(next_args: dict) -> Any:
try:
def _execute(next_args: dict) -> Any:
return agent._memory_manager.handle_tool_call(function_name, next_args)
function_result, function_args, middleware_trace, _execution_blocked = _managed_values(_run_agent_tool_execution_middleware(
function_result, function_args, middleware_trace, _execution_blocked, _execution_dispatched = _managed_values(_run_agent_tool_execution_middleware(
agent,
function_name=function_name,
function_args=function_args,
Expand Down Expand Up @@ -1755,6 +1827,7 @@ def _execute(next_args: dict) -> Any:
function_args,
middleware_trace,
_execution_blocked,
_execution_dispatched,
) = _managed_values(
_run_agent_tool_execution_middleware(
agent,
Expand Down Expand Up @@ -1833,6 +1906,7 @@ def _execute(next_args: dict) -> Any:
function_args,
middleware_trace,
_execution_blocked,
_execution_dispatched,
) = _managed_values(
_run_agent_tool_execution_middleware(
agent,
Expand Down Expand Up @@ -1895,7 +1969,10 @@ def _execute(next_args: dict) -> Any:
from agent.agent_runtime_helpers import agent_runtime_owns_post_tool_hook
_executor_must_emit_post_hook = (
not _execution_blocked
and agent_runtime_owns_post_tool_hook(agent, function_name)
and (
not _execution_dispatched
or agent_runtime_owns_post_tool_hook(agent, function_name)
)
)
if _executor_must_emit_post_hook:
_emit_terminal_post_tool_call(
Expand Down
16 changes: 16 additions & 0 deletions apps/desktop/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10510,6 +10510,22 @@ ipcMain.handle('hermes:writeClipboard', (_event, text) => {
return true
})

// Native save-location picker (profile export etc.) — the write itself happens
// elsewhere (the backend, for profile archives); this only picks the path.
ipcMain.handle('hermes:selectSavePath', async (_event, options: any = {}) => {
const result = await dialog.showSaveDialog(mainWindow, {
title: options?.title || 'Save',
defaultPath: options?.defaultPath ? String(options.defaultPath) : undefined,
filters: Array.isArray(options?.filters) ? options.filters : undefined
})

if (result.canceled || !result.filePath) {
return null
}

return result.filePath
})

// Paired reader for the GUI terminal's paste chord: the renderer's
// navigator.clipboard.readText() throws "Document is not focused" whenever a
// portaled overlay has focus, and there's no way to route a read through the
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ contextBridge.exposeInMainWorld('hermesDesktop', {
},
readFileText: filePath => ipcRenderer.invoke('hermes:readFileText', filePath),
selectPaths: options => ipcRenderer.invoke('hermes:selectPaths', options),
selectSavePath: options => ipcRenderer.invoke('hermes:selectSavePath', options),
writeClipboard: text => ipcRenderer.invoke('hermes:writeClipboard', text),
readClipboard: () => ipcRenderer.invoke('hermes:readClipboard'),
saveImageFromUrl: url => ipcRenderer.invoke('hermes:saveImageFromUrl', url),
Expand Down
Loading
Loading