Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
226 commits
Select commit Hold shift + click to select a range
4d0f2bd
fix(gateway): use FIFO queue for busy_input_mode pending messages
sweetcornna May 28, 2026
fec5ca7
fix: preserve telegram queue fifo during grace window
sweetcornna Jun 3, 2026
9f1c16a
fix(langfuse): restore usage/cost when post_api_request sends a sanit…
kamonspecial Jun 6, 2026
ccacfdb
fix(plugins): discover nested category plugins in 'plugins list' (iss…
islam666 Jun 7, 2026
8e71b51
fix(cli): paint approval/clarify/sudo/secret modal prompts directly, …
kshitijk4poor Jun 7, 2026
9d61076
fix: flush plugin-config OpenInference when the final session closes
mnajafian-nv Jun 7, 2026
ecd4679
fix(observability): preserve direct fallback until plugin-config init…
mnajafian-nv Jun 8, 2026
133e027
fix(slack): scope top-level channel messages by channel-only when rep…
briandevans Apr 25, 2026
ab0a627
fix(slack): align thread_ts check with is_thread_reply invariant (Cop…
briandevans Apr 25, 2026
09ec26c
fix(ollama): set default_max_tokens for custom/Ollama provider
islam666 Jun 5, 2026
38d1a41
chore: add islam666 to AUTHOR_MAP for salvaged PR #39624
teknium1 Jun 8, 2026
b18490b
fix(compaction): prevent infinite loop when transcript fits in tail b…
islam666 Jun 7, 2026
18c085b
fix(gateway): normalize optional systemd directives in stale-check (#…
islam666 Jun 7, 2026
41f0714
fix(vision): honor custom_providers per-model supports_vision (#41036)
islam666 Jun 7, 2026
9513793
fix(vision): proactive downgrade for providers rejecting list-type to…
islam666 Jun 7, 2026
f1d3afb
fix(profiles): skip 'default' in named profiles scan to prevent dupli…
islam666 Jun 5, 2026
2e61de0
fix(model_metadata): consult DEFAULT_CONTEXT_LENGTHS before 256K fall…
islam666 Jun 4, 2026
09a5548
fix(weixin): refresh typing ticket on expiry to prevent stuck indicat…
islam666 Jun 3, 2026
e53b74c
fix(dist): stop USER_OWNED_EXCLUDE from filtering nested directories
islam666 Jun 3, 2026
78e2101
fix: reap zombie subprocesses in web_server action status and meet_bo…
islam666 Jun 3, 2026
0c67d40
chore(release): map islam666 for as-is salvage batch
teknium1 Jun 8, 2026
ace4b72
feat(skills): add simplify-code skill — parallel 3-agent code review …
teknium1 Jun 8, 2026
53a2ac8
fix(desktop): unpack dist/ from asar so dashboard static files are se…
liuhao1024 Jun 7, 2026
bddc5fd
fix(desktop): fail loudly instead of blank-paging when the renderer b…
teknium1 Jun 8, 2026
48ae802
fix(delegate): resolve custom-endpoint subagent pools by endpoint ide…
teknium1 Jun 8, 2026
a77bc2c
fix(compression): disable compression on background-review fork to pr…
teknium1 Jun 8, 2026
5408013
fix(gateway): isolate DM sessions on user_id when chat_id is absent (…
teknium1 Jun 8, 2026
ad8e577
fix(hermes_time): implement reset_cache() referenced in docstrings (#…
teknium1 Jun 8, 2026
8513a6a
fix(compression): guard against cross-session stale _previous_summary…
basilalshukaili Jun 4, 2026
cca3b77
fix(compression): clear _previous_summary on session end (defense-in-…
dusterbloom Jun 8, 2026
b5f7a1f
chore(release): add basilalshukaili to AUTHOR_MAP
teknium1 Jun 8, 2026
2e62862
fix(telegram): use get_running_loop in polling-conflict retry resched…
teknium1 Jun 8, 2026
b8469a8
fix(weixin): add rate-limit circuit breaker
May 16, 2026
2a10da3
fix(gateway): keep /model + /reasoning overrides on topic recovery & …
teknium1 Jun 8, 2026
86c537d
fix(memory): instruct in-turn consolidation + retry on overflow (#41755)
teknium1 Jun 8, 2026
5487084
refactor(agent): extract run_conversation prologue into agent/turn_co…
teknium1 Jun 8, 2026
b2e6053
refactor(cli): extract hermes cron parser into hermes_cli/subcommands…
teknium1 Jun 8, 2026
4da45e8
refactor(cli): extract profile + gateway/proxy parsers into hermes_cl…
teknium1 Jun 8, 2026
568e127
refactor(cli): extract 25 more subcommand parsers into hermes_cli/sub…
teknium1 Jun 8, 2026
2789bf4
fix(auxiliary): route Codex Responses path through shared converter (…
teknium1 Jun 8, 2026
9b631e4
fix(acp): suppress cancel interrupt sentinel
lsaether May 24, 2026
f5bd09a
refactor(acp): share interrupt-sentinel prefix, simplify guard
teknium1 Jun 8, 2026
132d6fe
fix(volcengine): strip XML attribute fragments from tool_use.name (#3…
draix May 27, 2026
240c5d4
chore: map martin.alca@gmail.com -> draix in AUTHOR_MAP
teknium1 Jun 8, 2026
777dc9d
feat(acp): emit session provenance metadata for compression rotation …
teknium1 Jun 8, 2026
8e223b3
fix(curator): protect load-bearing built-in skills from archival/cons…
teknium1 Jun 8, 2026
cb5c24e
fix(agent): sync logging session context on compaction id rotation
JimStenstrom Jun 5, 2026
39c4ac3
chore(release): add AUTHOR_MAP entry for JimStenstrom
teknium1 Jun 8, 2026
6487069
test(gateway): add compression session_id rotation integration tests …
rodboev Jun 4, 2026
4d926f2
chore(release): add AUTHOR_MAP entry for rodboev
teknium1 Jun 8, 2026
524453d
refactor(agent): consolidate inner-retry-loop recovery flags into Tur…
teknium1 Jun 8, 2026
1a62647
refactor(cli): promote 9 closure handlers to top-level + extract thei…
teknium1 Jun 8, 2026
6459b3d
fix(terminal): collapse CWD-only overrides to shared container
liuhao1024 Jun 2, 2026
1c68f6f
refactor(gateway): extract kanban watcher loops into GatewayKanbanWat…
teknium1 Jun 8, 2026
9d6992e
Show platform sources in desktop sessions
dangelo352 Jun 6, 2026
ede4f5a
Show messaging source folders in desktop sessions
dangelo352 Jun 6, 2026
3fc67b7
Persist desktop sidebar drag order
dangelo352 Jun 6, 2026
0f500fc
Render grouped sessions when local list is empty
dangelo352 Jun 6, 2026
f0fcaa1
Preserve dragged order inside source folders
dangelo352 Jun 6, 2026
694adec
Smooth desktop sidebar drag sorting
dangelo352 Jun 6, 2026
d759c13
chore(salvage): lint fix + AUTHOR_MAP for desktop source-folders PR #…
teknium1 Jun 8, 2026
329c33d
fix(terminal): read cwd overrides under raw task_id after container c…
teknium1 Jun 8, 2026
3714caa
fix(session): follow compression continuations for transcript reads
konsisumer Jun 7, 2026
e02f4c0
fix(gateway): abort --replace when old PID survives SIGKILL
LeonSGP43 Jun 8, 2026
e45b745
fix(file-tools): reject sentinel TERMINAL_CWD; anchor worktree edits …
teknium1 Jun 8, 2026
d02a59b
fix(nix): cold npm builds + fix-lockfiles real-build verification + a…
alt-glitch Jun 8, 2026
4d18717
fix(gateway): drop --replace from systemd unit templates (#41892)
teknium1 Jun 8, 2026
4107076
Merge pull request #41155 from kshitijk4poor/fix/cli-modal-direct-inv…
kshitijk4poor Jun 8, 2026
02a4d66
fix(auxiliary): retry transient transport error once before fallback …
teknium1 Jun 8, 2026
619bd78
refactor(gateway): extract 42 slash-command handlers into GatewaySlas…
teknium1 Jun 8, 2026
de5fe2f
test(gateway): repoint slash-command mocks after mixin extraction
teknium1 Jun 8, 2026
7a5827c
test: repoint percentage-clamp source guard to gateway/slash_commands.py
teknium1 Jun 8, 2026
15c99b4
fix(cli): set PYTHON env for node-gyp native builds on NixOS (#40690)
flooryyyy Jun 8, 2026
039fbb4
fix(desktop): show newly configured model providers (#41545)
helix4u Jun 8, 2026
4eb8972
Merge pull request #33817 from sweetcornna/fix/28503-busy-input-fifo
kshitijk4poor Jun 8, 2026
0904bc7
refactor(cli): extract 32 slash-command handlers into CLICommandsMixi…
teknium1 Jun 8, 2026
fc0900d
fix(install): re-clone interrupted (commit-less) checkout instead of …
xxxigm Jun 7, 2026
5d7abf9
test(install): cover commit-less checkout handling (#40998)
xxxigm Jun 7, 2026
a5c12f5
fix(install): move broken checkout aside instead of deleting it
xxxigm Jun 7, 2026
aa6f277
fix(memory): run end-of-turn sync off the turn thread (#41945)
teknium1 Jun 8, 2026
dd0d122
fix(agent): don't retry interrupt-induced transport errors (cascading…
teknium1 Jun 8, 2026
8ae0d05
fix(tui): guard automatic heap dumps against disk fill
alarcritty May 8, 2026
00c46b8
test(tui): cover heapdump opt-in gate + retention; add AUTHOR_MAP
teknium1 Jun 8, 2026
d55304c
fix(gateway): transcribe voice messages during active agent runs
kristianvast Jun 8, 2026
f96eb85
chore: add kristianvast to AUTHOR_MAP
kshitijk4poor Jun 8, 2026
c3055d6
Merge pull request #41984 from kshitijk4poor/salvage/6600-stale-strea…
kshitijk4poor Jun 8, 2026
2b89afe
fix(plugins): alias-normalize enable/disable for nested category plug…
kshitijk4poor Jun 8, 2026
b99c6c4
Merge #42076: nested category plugin discovery + alias-normalized ena…
kshitijk4poor Jun 8, 2026
400e6e4
test(gateway): de-flake concurrent-compression lock test with a barrier
teknium1 Jun 8, 2026
3d029a5
fix(gateway): close residual memory-leak sites under heavy scheduled …
mssteuer Jun 8, 2026
e9c1e75
fix(gateway): release evicted agent clients to stop RSS leak (#29298)…
teknium1 Jun 8, 2026
b31c6c3
fix(pty-bridge): terminate PTY process groups on teardown
paulb26 May 12, 2026
8b6a8f6
feat(slash-worker): self-terminate on parent death via create_time wa…
banditburai May 30, 2026
8cb1908
chore: map paulb26 in AUTHOR_MAP for #24135 salvage
teknium1 Jun 8, 2026
cb13723
fix(pty-bridge): mark os.killpg/getpgid windows-footgun-ok (POSIX-onl…
teknium1 Jun 8, 2026
5e06c9f
fix(agent): clear _session_messages in AIAgent.close() (#42123)
teknium1 Jun 8, 2026
a3fca26
fix(tui): close slash_worker inside _finalize_session (defense-in-dep…
teknium1 Jun 8, 2026
4219a91
fix(nix): make config.yaml group-writable under addToSystemPackages (…
alt-glitch Jun 8, 2026
728612c
fix(observability): recover after plugin-config clear failure
mnajafian-nv Jun 8, 2026
7230fcb
revert(nix): drop the cp patchPhase workaround from #41867 (#42151)
alt-glitch Jun 8, 2026
fd1e7c2
fix(tui): install the process.on('exit') terminal-mode backstop (#42165)
teknium1 Jun 8, 2026
9e36068
feat(dashboard): return recent commits from /api/hermes/update/check
yoniebans Jun 6, 2026
2284147
docs: document commits field on /api/hermes/update/check
yoniebans Jun 6, 2026
ed1e253
feat(desktop): show client and backend versions in status bar when re…
yoniebans Jun 6, 2026
64da518
feat(desktop): remote update overlay sourced from backend
yoniebans Jun 6, 2026
87ac7ca
fix(dashboard): log update changelog against origin/main, not @{upstr…
yoniebans Jun 6, 2026
9c26455
fix(desktop): name the update target in the overlay; honest no-change…
yoniebans Jun 6, 2026
56be1a6
fix(desktop): split client and backend into two distinct update buttons
yoniebans Jun 6, 2026
cfaa46f
fix(desktop): pre-check backend updates in poller; client button first
yoniebans Jun 6, 2026
47518bc
fix(desktop): check backend updates when the connection becomes remote
yoniebans Jun 6, 2026
9b2a64f
fix(desktop): reflect env-override remote in gateway connection state
yoniebans Jun 7, 2026
8164745
fix(desktop): recover the backend update overlay after the remote res…
yoniebans Jun 8, 2026
cd030f5
fix(desktop): close the backend update overlay on success; error on n…
yoniebans Jun 8, 2026
b000e05
fix(desktop): don't claim the backend update succeeded when it never …
yoniebans Jun 8, 2026
74239b4
i18n(desktop): translate backend update apply status messages
yoniebans Jun 8, 2026
47d5177
fix(plugins): thread-safe lazy-singleton helpers; fix honcho TOCTOU (…
teknium1 Jun 8, 2026
399b8ee
fix(anthropic): strip Responses-only kwargs before Messages SDK call …
teknium1 Jun 8, 2026
7474479
docs(tui): correct HERMES_TUI_GATEWAY_URL — dashboard-internal, not r…
teknium1 Jun 8, 2026
cef00ae
fix(tui): handle Windows PTY stdin and detached WS frames (#41953)
qWaitCrypto Jun 8, 2026
094aa85
refactor(cli): extract agent-construction cluster into CLIAgentSetupM…
teknium1 Jun 8, 2026
a706a34
refactor(gateway): extract authorization cluster into GatewayAuthoriz…
teknium1 Jun 8, 2026
55b83c3
refactor(agent): extract run_conversation post-loop tail into finaliz…
teknium1 Jun 8, 2026
a77efad
refactor(cli): extract 18 model-flow wizard functions into model_setu…
teknium1 Jun 8, 2026
de80d28
fix(desktop): require session ids for scoped gateway events (#42178)
OutThisLife Jun 8, 2026
9c9d911
fix(auth): auto-detect OpenRouter credential from the pool, not just …
teknium1 Jun 8, 2026
ae94ed1
fix(tui-gateway): reap leaked slash_worker sessions on disconnect + a…
banditburai Jun 8, 2026
365813a
fix: resolve rebase conflict in _teardown_session worker cleanup
teknium1 Jun 8, 2026
a38003b
Merge pull request #42143 from kshitijk4poor/salvage/tui-slash-worker…
kshitijk4poor Jun 8, 2026
395ed91
fix(desktop): keep a just-finished session visible after switching aw…
OutThisLife Jun 8, 2026
9b1e0d6
feat(desktop): assignable themes per profile (#42286)
OutThisLife Jun 8, 2026
8e4c447
fix(gateway): prevent duplicate user messages in state.db
liuhao1024 Jun 8, 2026
4129092
fix(cli): strip OSC 8 hyperlink sequences in ChatConsole output
rbrtbn May 25, 2026
550b72d
fix(cli): gate tool-rendering paths with tool_progress_mode, not quie…
JezzaHehn Jun 8, 2026
5916248
chore: add AUTHOR_MAP entry for rbrtbn (salvage #25939)
teknium1 Jun 8, 2026
c6d27ad
fix(deps): align aiohttp extras pins with lazy Slack pin (3.13.4)
cresslank Jun 8, 2026
abcf996
feat(windows): enable dashboard /chat tab via ConPTY (win_pty_bridge)…
teknium1 Jun 8, 2026
021d103
fix(nemo-relay): align adaptive config with tool_parallelism mode
mnajafian-nv Jun 8, 2026
96fd9d4
fix(desktop): stop running Hermes.exe locking win-unpacked before Win…
xxxigm Jun 8, 2026
b0efe1d
fix(approval): gate resolved Hermes config paths
helix4u Jun 7, 2026
89d3802
fix(approval): resolve Hermes home at detection time, not import time
teknium1 Jun 8, 2026
c9094f5
fix(stream): don't report dropped mid-tool-call streams as output tru…
teknium1 Jun 8, 2026
761b744
fix(auth): preserve independent Codex pool entries on re-auth (#39236)
temalo Jun 4, 2026
c78b3e1
fix(auth): add Codex OAuth accounts as distinct pool entries
teknium1 Jun 8, 2026
2f510ca
fix(deps): align anthropic extra pin with lazy pin + guard whole pin …
teknium1 Jun 8, 2026
e881162
fix(update): scope git fetch to target branch
OutThisLife Jun 7, 2026
6e7033b
fix(desktop): don't drop the focused chat's own stream when unscoped …
OutThisLife Jun 8, 2026
5b4e431
feat(gateway): add Photon Spectrum (iMessage) platform plugin
teknium1 May 26, 2026
3a0f6ac
fix(photon): satisfy Windows footgun + CodeQL checks
teknium1 May 26, 2026
91db0ab
fix(photon): clear remaining CodeQL clear-text-{logging,storage} alerts
teknium1 May 26, 2026
55fb422
fix(photon): isolate ALL secret-touching prints behind auth.py helpers
teknium1 May 26, 2026
2ee7abf
fix(photon): emit credential summary via callback so no tainted value…
teknium1 May 26, 2026
6a0cc9b
fix(photon): suppress CodeQL clear-text-logging false-positives in au…
teknium1 May 26, 2026
083d8b2
fix(photon): collapse credential summary to single-emit literal-blob
teknium1 May 26, 2026
8f89c46
chore(photon): clean up ty type-checker warnings from lint-diff bot
teknium1 May 27, 2026
630318e
refactor(photon): fold device login into setup, drop standalone login…
teknium1 Jun 8, 2026
d7f42e3
feat(photon): full channel parity — gateway setup, pairing, PII redac…
teknium1 Jun 8, 2026
1866518
feat(photon): group-chat mention gating for full channel parity
teknium1 Jun 8, 2026
754154a
fix(tests): retry per-file pytest subprocess once on exit-4 when the …
teknium1 Jun 8, 2026
a1cb84a
chore(release): add mnajafian-nv to AUTHOR_MAP
kshitijk4poor Jun 8, 2026
9fd3d5c
Merge pull request #42380 from kshitijk4poor/chore/author-map-mnajafian
kshitijk4poor Jun 8, 2026
cf49630
Merge branch 'main' into fix/hermes-plugin-openinference-finalization
kshitijk4poor Jun 8, 2026
3f1758d
Merge pull request #41551 from mnajafian-nv/fix/hermes-plugin-openinf…
kshitijk4poor Jun 8, 2026
d6c11a4
test(run_agent): fix racy ordering in test_concurrent_handles_tool_er…
teknium1 Jun 8, 2026
1db79bf
Merge branch 'main' into fix/nemo-relay-adaptive-config-shape
kshitijk4poor Jun 8, 2026
d3992d1
Merge pull request #42331 from mnajafian-nv/fix/nemo-relay-adaptive-c…
kshitijk4poor Jun 8, 2026
09a6a2d
fix(desktop): stream the transcript while the window is backgrounded …
OutThisLife Jun 8, 2026
1e3b3df
Merge pull request #40560 from kamonspecial/fix/langfuse-usage-saniti…
kshitijk4poor Jun 8, 2026
639c1e3
feat(sessions): add optional max session cap
rewbs Jun 8, 2026
5e9d7a7
fix(skills-hub): stop shipping a degenerate index when GitHub taps co…
teknium1 Jun 8, 2026
4615e08
feat(photon): wire outbound media via spectrum-ts attachment() (#42397)
teknium1 Jun 8, 2026
37561c2
fix(photon): use allowlisted device client_id + validate token before…
rayshineeeee Jun 8, 2026
421226e
fix(gateway): stop terminal progress from posting the full command to…
GodsBoy Jun 8, 2026
732abab
fix(doctor): allow vendor slugs for named custom providers
helix4u Jun 8, 2026
aa424e5
refactor(doctor): fold custom-provider vendor-slug check into one pre…
teknium1 Jun 8, 2026
6d2732e
fix(gateway): apply MarkdownV2 formatting on progress message edits
ruangraung Jun 8, 2026
f4531fe
fix(telegram): improve MarkdownV2 edit fallback and fix _strip_mdv2 b…
ruangraung Jun 8, 2026
300371c
chore: add AUTHOR_MAP entry for ruangraung (PR #42308 salvage)
teknium1 Jun 8, 2026
d66bac5
test(cli): failing regression test for native-Windows confirm deadloc…
banditburai May 31, 2026
ab98818
fix(cli): use the confirm modal on native Windows instead of deadlock…
banditburai May 31, 2026
7141835
test(cli): convert stale win32 stdin-fallback tests to the modal cont…
banditburai May 31, 2026
b5f8996
test(cli): exercise real _prompt_text_input for native-Windows confir…
teknium1 Jun 8, 2026
e0f6a35
fix(desktop): render debug-report paste URLs as real clickable links
jquesnelle Jun 9, 2026
6a8dda1
Merge pull request #42515 from NousResearch/fix/desktop-debug-report-…
jquesnelle Jun 9, 2026
1e5ff4a
fix(hermes-ink): disable mouse tracking on raw-mode teardown to stop …
OutThisLife Jun 9, 2026
52ae9d9
feat(dashboard): make `hermes dashboard register` idempotent (#42455)
benbarclay Jun 9, 2026
b23184c
fix(api-server): bind request session context for tools
helix4u Jun 8, 2026
a46462e
fix(cli): persist custom --portal-url to .env on dashboard register (…
benbarclay Jun 9, 2026
0c2e81d
feat(simplex): groups, native attachments, text batching, auto-accept
jooray Jun 9, 2026
c3420d9
chore: add jooray to AUTHOR_MAP for salvaged simplex PR #27978
teknium1 Jun 9, 2026
4e4d278
feat(photon): gRPC-native iMessage channel (no webhook)
underthestars-zhy Jun 8, 2026
b3aef57
refactor(photon): use TYPE_CHECKING for httpx import and fix client ref
underthestars-zhy Jun 8, 2026
314af28
feat(photon): download and inline inbound attachments
underthestars-zhy Jun 9, 2026
84e4b4b
fix(photon): use per-user assigned line for agent iMessage number
underthestars-zhy Jun 9, 2026
e9b26c7
style(photon): Colorize iMessage number box in setup output
underthestars-zhy Jun 9, 2026
9217935
feat(photon): auto-configure allowlist and cron channel on setup
underthestars-zhy Jun 9, 2026
0646656
fix(photon): support E.164 and DM GUID targets for home channel
underthestars-zhy Jun 9, 2026
fdf48c6
fix(photon): wrap text sends with spectrumText helper
underthestars-zhy Jun 9, 2026
e79e44a
fix(photon): use spectrum-ts reply builder for threaded messages
underthestars-zhy Jun 9, 2026
0d25cae
fix(photon): remove reply-to support and fix typing API
underthestars-zhy Jun 9, 2026
3b983e7
fix(photon): add home channel env seed and simplify space resolution
underthestars-zhy Jun 9, 2026
3dcfbbf
chore(release): add underthestars-zhy to AUTHOR_MAP
teknium1 Jun 9, 2026
3705625
feat(gateway): render terminal commands as bare fenced code blocks in…
teknium1 Jun 9, 2026
c1927d2
fix(desktop): set tsconfig lib/target to ES2023 for findLast/findLast…
xxxigm Jun 4, 2026
54318c6
feat(models): seed model-catalog disk cache from checkout on update (…
teknium1 Jun 9, 2026
d1f23bb
fix: prevent TUI gateway stdin EOF crash across all TUI-context subpr…
m4dni5 Jun 8, 2026
bddab61
ci: add subprocess stdin= regression check for TUI-context code
m4dni5 Jun 8, 2026
8bb60ff
test: add pytest guard for subprocess stdin= in TUI-context code
m4dni5 Jun 8, 2026
2c1aaa9
fix: keep interactive OAuth setup-token inheriting stdin
teknium1 Jun 9, 2026
ba622d4
chore(release): add AUTHOR_MAP entry for m4dni5
teknium1 Jun 9, 2026
dba6380
test: guard OAuth setup-token stays interactive + marker exemption
teknium1 Jun 9, 2026
18ead88
test: update docker preflight assertion for stdin=DEVNULL kwarg
teknium1 Jun 9, 2026
9351cba
fix(gateway): auto-deliver image_generate output as native media (#42…
teknium1 Jun 9, 2026
637cf94
fix(photon): strip markdown and add send retry logic
underthestars-zhy Jun 9, 2026
2130ef6
fix(photon): Enable group flattening in Spectrum config
underthestars-zhy Jun 9, 2026
b58ff93
feat(photon): persist and display user phone numbers in status
underthestars-zhy Jun 9, 2026
0337658
fix(photon): migrate user API calls to Spectrum backend
underthestars-zhy Jun 9, 2026
9fb83ea
fix(photon): bump spectrum-ts to ^1.18.0 and always install latest on
underthestars-zhy Jun 9, 2026
dbf2470
feat(photon): Add voice message support to Photon adapter
underthestars-zhy Jun 9, 2026
4b073d0
fix(tui): preserve fallback provider chain
psionic73 Apr 30, 2026
520b59d
fix(tui): use canonical get_fallback_chain for parity + map author
teknium1 Jun 9, 2026
50ad191
test(hermes_cli): harden concurrent-gate fixture against partial-impo…
teknium1 Jun 9, 2026
c406609
feat(models): add laguna-m.1 + nemotron-3-ultra to curated OpenRouter…
teknium1 Jun 9, 2026
e687292
feat(models): persist Nous recommended-models to disk; fall back on P…
teknium1 Jun 9, 2026
dbbd1d4
feat(desktop+gateway): remote-gateway file attachments via file.attach
teknium1 Jun 9, 2026
f8adefd
fix(tui): apply terminal backend config before launch
helix4u Jun 9, 2026
da5bcc2
chore: merge upstream main into apecloud-base
1aal Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 16 additions & 6 deletions .github/workflows/deploy-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,22 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Always rebuild — the file isn't committed (gitignored), so a
# fresh checkout starts without it and we want the freshest crawl
# in every deploy. Failure is non-fatal: extract-skills.py will
# fall back to the legacy snapshot cache and the Skills Hub page
# still renders, just without the latest community catalog.
python3 scripts/build_skills_index.py || echo "Skills index build failed (non-fatal)"
# Rebuild the unified catalog. The file is gitignored, so a fresh
# checkout starts without it and we want the freshest crawl in
# every deploy.
#
# This MUST be fatal. build_skills_index.py runs a health check and
# exits non-zero WITHOUT writing the output file when a source
# collapses (e.g. a GitHub API rate limit zeroes the github /
# claude-marketplace / well-known taps all at once). Letting the
# deploy continue would either (a) ship a degenerate index missing
# whole hubs — the June 2026 regression where OpenAI/Anthropic/
# HuggingFace/NVIDIA tabs vanished — or (b) fall through to a
# local-only catalog. Failing here keeps the last good deployment
# live (GitHub Pages serves the previous build) instead of
# publishing a broken catalog. Re-run the workflow once the
# transient rate limit clears.
python3 scripts/build_skills_index.py

- name: Extract skill metadata for dashboard
run: python3 website/scripts/extract-skills.py
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/nix-lockfile-fix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ jobs:
run: |
set -euo pipefail

# Ensure only nix files were modified — prevents accidental
# self-triggering if fix-lockfiles ever touches package files.
unexpected="$(git diff --name-only | grep -Ev '^nix/(tui|web)\.nix$' || true)"
# Ensure only nix/lib.nix (home of the single npmDepsHash) was
# modified — prevents accidental self-triggering if fix-lockfiles
# ever touches package files.
unexpected="$(git diff --name-only | grep -Ev '^nix/lib\.nix$' || true)"
if [ -n "$unexpected" ]; then
echo "::error::Unexpected modified files: $unexpected"
exit 1
Expand All @@ -89,7 +90,7 @@ jobs:

git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add nix/tui.nix nix/web.nix
git add nix/lib.nix
git commit -m "fix(nix): auto-refresh npm lockfile hashes" \
-m "Source: $GITHUB_SHA" \
-m "Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
Expand Down Expand Up @@ -216,7 +217,7 @@ jobs:
set -euo pipefail
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add nix/tui.nix nix/web.nix
git add nix/lib.nix
git commit -m "fix(nix): refresh npm lockfile hashes"
git push

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ If you already have Git installed, the installer detects it and uses that instea

> **Android / Termux:** The tested manual path is documented in the [Termux guide](https://hermes-agent.nousresearch.com/docs/getting-started/termux). On Termux, Hermes installs a curated `.[termux]` extra because the full `.[all]` extra currently pulls Android-incompatible voice dependencies.
>
> **Windows:** Native Windows is fully supported — the PowerShell one-liner above installs everything. If you'd rather use WSL2, the Linux command works there too. Native Windows install lives under `%LOCALAPPDATA%\hermes`; WSL2 installs under `~/.hermes` as on Linux. The only Hermes feature that currently needs WSL2 specifically is the browser-based dashboard chat pane (it uses a POSIX PTY — classic CLI and gateway both run natively).
> **Windows:** Native Windows is fully supported — the PowerShell one-liner above installs everything. If you'd rather use WSL2, the Linux command works there too. Native Windows install lives under `%LOCALAPPDATA%\hermes`; WSL2 installs under `~/.hermes` as on Linux.

After installation:

Expand Down
127 changes: 127 additions & 0 deletions acp_adapter/provenance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"""Derive ACP session-provenance metadata from the existing compression chain.

This is an additive Hermes extension surfaced under ACP ``_meta.hermes`` so
existing ACP clients ignore it. It carries no new persisted state: everything
is derived on demand from the ``sessions`` table (``parent_session_id`` /
``end_reason``), which already models compression-continuation chains.

The ACP/editor ``session_id`` stays the stable public handle. When context
compression rotates the internal Hermes head, ``build_session_provenance`` lets
a client see the previous/current internal ids and the lineage root without
parsing status text, guessing from token drops, or reading ``state.db``.
"""

from __future__ import annotations

from typing import Any, Dict, Optional

# Bound defensive walks; compression chains this deep are pathological.
_MAX_WALK = 100


def build_session_provenance(
db: Any,
acp_session_id: str,
current_hermes_session_id: str,
*,
previous_hermes_session_id: Optional[str] = None,
) -> Optional[Dict[str, Any]]:
"""Build ``_meta.hermes.sessionProvenance`` for an ACP session.

Args:
db: A ``SessionDB`` (must expose ``get_session``).
acp_session_id: The stable ACP/editor-facing session handle.
current_hermes_session_id: The live internal Hermes DB session id
(``state.agent.session_id``).
previous_hermes_session_id: The internal id from before the most recent
turn, when known. Supplied by ``prompt()`` to flag a rotation.

Returns:
A dict suitable for ``{"hermes": {"sessionProvenance": <dict>}}`` under
ACP ``_meta``, or ``None`` if the session can't be read.
"""
try:
row = db.get_session(current_hermes_session_id)
except Exception:
return None
if not row:
return None

parent_id = row.get("parent_session_id")
end_reason = row.get("end_reason")

# Walk parents to the lineage root and count compression depth. Only
# compression-split parents (parent.end_reason == 'compression') count
# toward depth — delegate/branch children share the parent_session_id
# column but are not compaction boundaries.
root_id = current_hermes_session_id
compression_depth = 0
cursor_parent = parent_id
seen = {current_hermes_session_id}
for _ in range(_MAX_WALK):
if not cursor_parent or cursor_parent in seen:
break
seen.add(cursor_parent)
try:
prow = db.get_session(cursor_parent)
except Exception:
prow = None
if not prow:
break
root_id = cursor_parent
if prow.get("end_reason") == "compression":
compression_depth += 1
cursor_parent = prow.get("parent_session_id")

# A session is a compression continuation when its parent was ended with
# end_reason='compression'. Determine that from the immediate parent.
is_continuation = False
if parent_id:
try:
immediate_parent = db.get_session(parent_id)
except Exception:
immediate_parent = None
if immediate_parent and immediate_parent.get("end_reason") == "compression":
is_continuation = True

rotated = bool(
previous_hermes_session_id
and previous_hermes_session_id != current_hermes_session_id
)

provenance: Dict[str, Any] = {
"acpSessionId": acp_session_id,
"currentHermesSessionId": current_hermes_session_id,
"rootHermesSessionId": root_id,
"parentHermesSessionId": parent_id,
"sessionKind": "continuation" if is_continuation else "root",
"compressionDepth": compression_depth,
}
if previous_hermes_session_id:
provenance["previousHermesSessionId"] = previous_hermes_session_id
if rotated:
# The head moved during the last turn. The only mechanism that rotates
# the internal id mid-turn is compression-driven session splitting.
provenance["reason"] = "compression"
provenance["creatorKind"] = "compression"

return provenance


def session_provenance_meta(
db: Any,
acp_session_id: str,
current_hermes_session_id: str,
*,
previous_hermes_session_id: Optional[str] = None,
) -> Optional[Dict[str, Any]]:
"""Return a ready ``_meta`` payload: ``{"hermes": {"sessionProvenance": ...}}``."""
prov = build_session_provenance(
db,
acp_session_id,
current_hermes_session_id,
previous_hermes_session_id=previous_hermes_session_id,
)
if prov is None:
return None
return {"hermes": {"sessionProvenance": prov}}
100 changes: 95 additions & 5 deletions acp_adapter/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
make_tool_progress_cb,
)
from acp_adapter.permissions import make_approval_callback
from acp_adapter.provenance import session_provenance_meta
from acp_adapter.session import SessionManager, SessionState, _expand_acp_enabled_toolsets
from acp_adapter.tools import build_tool_complete, build_tool_start

Expand Down Expand Up @@ -709,8 +710,39 @@ async def _send_usage_update(self, state: SessionState) -> None:
exc_info=True,
)

async def _send_session_info_update(self, session_id: str) -> None:
"""Send ACP native session metadata after Hermes changes it."""
def _provenance_meta(
self,
acp_session_id: str,
current_hermes_session_id: str,
previous_hermes_session_id: Optional[str] = None,
) -> Optional[dict]:
"""Best-effort ``_meta.hermes.sessionProvenance`` for an ACP session."""
try:
return session_provenance_meta(
self.session_manager._get_db(),
acp_session_id,
current_hermes_session_id,
previous_hermes_session_id=previous_hermes_session_id,
)
except Exception:
logger.debug(
"Could not build ACP session provenance for %s", acp_session_id, exc_info=True
)
return None

async def _send_session_info_update(
self,
session_id: str,
*,
current_hermes_session_id: Optional[str] = None,
previous_hermes_session_id: Optional[str] = None,
) -> None:
"""Send ACP native session metadata after Hermes changes it.

When the internal Hermes head rotated (e.g. compression-driven session
split during a turn), pass ``previous_hermes_session_id`` so the
attached ``_meta.hermes.sessionProvenance`` flags the rotation reason.
"""
if not self._conn:
return
try:
Expand All @@ -727,10 +759,16 @@ async def _send_session_info_update(self, session_id: str) -> None:
# the updated_at since we're emitting this notification precisely
# because the title was just refreshed.
updated_at = datetime.now(timezone.utc).isoformat()
meta = self._provenance_meta(
session_id,
current_hermes_session_id or session_id,
previous_hermes_session_id,
)
update = SessionInfoUpdate(
session_update="session_info_update",
title=title if isinstance(title, str) and title.strip() else None,
updated_at=updated_at,
field_meta=meta,
)
try:
await self._conn.session_update(
Expand Down Expand Up @@ -1081,6 +1119,9 @@ async def new_session(
session_id=state.session_id,
models=self._build_model_state(state),
modes=self._session_modes(state),
field_meta=self._provenance_meta(
state.session_id, getattr(state.agent, "session_id", state.session_id)
),
)

async def load_session(
Expand Down Expand Up @@ -1125,6 +1166,9 @@ async def load_session(
return LoadSessionResponse(
models=self._build_model_state(state),
modes=self._session_modes(state),
field_meta=self._provenance_meta(
session_id, getattr(state.agent, "session_id", session_id)
),
)

async def resume_session(
Expand Down Expand Up @@ -1157,6 +1201,9 @@ async def resume_session(
return ResumeSessionResponse(
models=self._build_model_state(state),
modes=self._session_modes(state),
field_meta=self._provenance_meta(
state.session_id, getattr(state.agent, "session_id", state.session_id)
),
)

async def cancel(self, session_id: str, **kwargs: Any) -> None:
Expand Down Expand Up @@ -1494,6 +1541,11 @@ def _run_agent() -> dict:
logger.debug("Could not clear ACP session context", exc_info=True)

try:
# Snapshot the internal Hermes DB session id before the turn so we
# can detect a compression-driven session rotation afterwards. The
# ACP `session_id` stays the stable client handle; agent.session_id
# is the live internal head that compression may rotate.
pre_turn_hermes_id = getattr(state.agent, "session_id", None)
# Wrap the executor call in a fresh copy of the current context so
# concurrent ACP sessions on the shared ThreadPoolExecutor don't
# stomp on each other's ContextVar writes (HERMES_SESSION_KEY in
Expand All @@ -1512,8 +1564,41 @@ def _run_agent() -> dict:
# Persist updated history so sessions survive process restarts.
self.session_manager.save_session(session_id)

# Detect a compression-driven internal session rotation. If the agent's
# DB head moved during the turn, emit a session_info_update carrying
# _meta.hermes.sessionProvenance so ACP clients can render the boundary
# and keep old/new ids in lineage. The ACP session_id is unchanged.
post_turn_hermes_id = getattr(state.agent, "session_id", None)
if (
conn
and post_turn_hermes_id
and pre_turn_hermes_id
and post_turn_hermes_id != pre_turn_hermes_id
):
try:
await self._send_session_info_update(
session_id,
current_hermes_session_id=post_turn_hermes_id,
previous_hermes_session_id=pre_turn_hermes_id,
)
except Exception:
logger.debug(
"Could not emit ACP provenance update after rotation for %s",
session_id,
exc_info=True,
)

final_response = result.get("final_response", "")
if final_response:
cancelled = bool(state.cancel_event and state.cancel_event.is_set())
interrupted = bool(result.get("interrupted")) or cancelled
# Hermes' local "waiting for model response" interrupt status is metadata,
# not assistant prose — clients get cancellation from stop_reason instead.
from agent.conversation_loop import INTERRUPT_WAITING_FOR_MODEL_PREFIX

suppress_interrupt_response = interrupted and final_response.startswith(
INTERRUPT_WAITING_FOR_MODEL_PREFIX
)
if final_response and not suppress_interrupt_response:
try:
from agent.title_generator import maybe_auto_title

Expand All @@ -1534,7 +1619,12 @@ def _notify_title_update(_title: str) -> None:
)
except Exception:
logger.debug("Failed to auto-title ACP session %s", session_id, exc_info=True)
if final_response and conn and (not streamed_message or result.get("response_transformed")):
if (
final_response
and conn
and not suppress_interrupt_response
and (not streamed_message or result.get("response_transformed"))
):
# Deliver the final response when streaming did not already send it,
# or when a plugin hook transformed the response after streaming
# finished (e.g. transform_llm_output) — otherwise the appended /
Expand Down Expand Up @@ -1576,7 +1666,7 @@ def _notify_title_update(_title: str) -> None:

await self._send_usage_update(state)

stop_reason = "cancelled" if state.cancel_event and state.cancel_event.is_set() else "end_turn"
stop_reason = "cancelled" if cancelled else "end_turn"
return PromptResponse(stop_reason=stop_reason, usage=usage)

# ---- Slash commands (headless) -------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions agent/agent_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ def init_agent(
save_trajectories: bool = False,
verbose_logging: bool = False,
quiet_mode: bool = False,
tool_progress_mode: str = "all",
ephemeral_system_prompt: str = None,
log_prefix_chars: int = 100,
log_prefix: str = "",
Expand Down Expand Up @@ -280,6 +281,7 @@ def init_agent(
agent.save_trajectories = save_trajectories
agent.verbose_logging = verbose_logging
agent.quiet_mode = quiet_mode
agent.tool_progress_mode = tool_progress_mode
agent.ephemeral_system_prompt = ephemeral_system_prompt
agent.platform = platform # "cli", "telegram", "discord", "whatsapp", etc.
agent._user_id = user_id # Platform user identifier (gateway sessions)
Expand Down
Loading
Loading