Commit graph

15137 commits

Author SHA1 Message Date
joaomarcos
83b2a685cd fix(codex): also guard the auxiliary Copilot Responses adapter
_CodexCompletionsAdapter (agent/auxiliary_client.py) is a second,
independent producer of Codex Responses input — used by auxiliary
calls (context compression, flush_memories, MoA aggregation,
session_search) that route through CodexAuxiliaryClient instead of
the main agent's ResponsesApiTransport.build_kwargs. It calls
_chat_messages_to_responses_input() directly without is_github_responses,
so the previous commit's fix didn't cover it: an auxiliary call made
against a Copilot-backed session could still replay a connection-scoped
codex_message_items id and hit the same HTTP 401.

Detect the Copilot host from the adapter's own client.base_url (same
check the adapter already does further down for prompt_cache_key
opt-out) and pass is_github_responses through, closing the gap.

Still #32716.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 12:09:27 +05:30
joaomarcos
b9146a47bc fix(codex): never replay message-item id on Copilot Responses connections
Copilot (api.githubcopilot.com/responses) binds replayed assistant
codex_message_items ids to a specific backend "connection". Credential-
pool rotation, a gateway restart, or routine load-balancer churn between
turns all invalidate that binding, and Copilot rejects the stale id with
HTTP 401 "input item ID does not belong to this connection" — even for
short ids well under the #27038 64-char length cap, since this is a
connection-scope problem, not a length problem. Once a session captures
one of these ids it is persisted and replayed forever, permanently
bricking the session.

Thread an is_github_responses flag from build_kwargs/convert_messages
into _chat_messages_to_responses_input and drop the id unconditionally
on that path, mirroring how reasoning items already strip id on replay.
phase/status/content are still replayed so cache-relevant signal isn't
lost — only the connection-scoped id is unsafe to reuse.

Written to apply independently of the #27038 length-cap fix so the two
PRs don't block each other; they touch adjacent conditions in the same
block and merge cleanly in either order.

Fixes #32716

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 12:09:27 +05:30
kshitijk4poor
4aa499ff9f fix(telegram): harden flood fallback recovery
Keep empty-tail recovery scoped to the current stream segment and bound fallback flood retries. Preserve Telegram's server retry hint without blocking final delivery through a long cooldown.
2026-07-11 11:13:50 +05:30
Gille
04898631cb fix(telegram): recover final delivery after stream flood 2026-07-11 11:13:50 +05:30
kshitijk4poor
02063ece11 fix(cron): scope inline calls to reported transport 2026-07-11 11:07:34 +05:30
kshitijk4poor
47c91e4c34 fix(cron): abort inline requests on timeout 2026-07-11 11:07:34 +05:30
kshitijk4poor
8acac440f4 fix(cron): keep inline dispatch behind the agent call seam 2026-07-11 11:07:34 +05:30
PRATHAMESH75
5c5dd6b7ec fix(agent): run cron LLM calls inline to avoid gateway deadlock (#62151) 2026-07-11 11:07:34 +05:30
HexLab98
1234f39e31 test(cron): guard direct API path for gateway deadlock (#62151) 2026-07-11 11:07:34 +05:30
HexLab98
d00c15c0c5 fix(cron): run gateway cron LLM calls synchronously (#62151)
Cron jobs in the gateway process wedged before HTTP on later non-streaming
API calls because interruptible_api_call spawned a daemon worker inside
nested cron thread pools. Route cron platform turns through direct_api_call
on the conversation thread instead.
2026-07-11 11:07:34 +05:30
HexLab98
7acaff5ef2 fix(desktop): pin session context during async prompt submit (#54527)
Snapshot the selected stored session and route token for the full async submit
pipeline so a mid-flight session switch cannot resume the wrong chat or
misroute the user's text. Includes regression tests.
2026-07-10 19:35:05 -07:00
joaomarcos
2afa92c74f fix(desktop): pin composer draft scope to the swap-effect owner, not the render ref
Fixes #54527 — a message typed into one TUI session could be silently
misrouted into (or overwritten by) another concurrently-open session.

Root cause: activeQueueSessionKeyRef is written on every render, but the
debounced draft-persist timer, the pagehide flush, and dispatchSubmit's
reject-restore path all read it lazily at async-resolve time instead of
capturing the scope that was active when the operation started. A session
switch landing between capture and resolve relabels one session's text
under the other session's key. A large paste widens the window (slower
synchronous render), which matches the original report.

Fix: introduce draftScopeRef, written only by the draft-swap effect (so it
always reflects the session whose text is actually loaded in the editor)
and read it instead of the render-time ref at both async write sites.
dispatchSubmit's restore() now uses the submittedScope already captured at
dispatch instead of re-reading the live ref.

Also adds isPendingDraftPersistCurrent as defense-in-depth: before the
debounce timer commits a write, it verifies its captured {scope, text}
pair is still the one on file. This is a no-op under the fix above (a
session swap or a newer keystroke already clears/replaces the pending
entry via clearTimeout), but turns any future regression that reintroduces
a stale/live-ref read at this call site into a dropped write instead of a
silent cross-session misroute.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 19:35:05 -07:00
joaomarcos
0b2907f586 fix(codex): drop oversized message ids on Responses input replay
Codex assigns assistant message items server-side ids that can run
400+ chars (base64 encrypted blobs), but the Responses API caps
input[].id at 64 chars and rejects the whole request with a
non-retryable HTTP 400. Once a session captures one of these long
ids, every subsequent turn replays it and 400s forever, since the
history persists it in codex_message_items.

Add a 64-char length guard at both replay sites — the history-to-
input converter and the final preflight gate — so oversized ids are
dropped while short ids (msg_...) are kept for prefix-cache hits.
Mirrors the existing pattern for reasoning items, which already
strip their id before replay because store=False means the API
can't resolve ids server-side anyway.

Fixes #27038

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 19:26:08 -07:00
Teknium
dabae386ef
fix(cron): bind claim heartbeats to dispatch owner (#62155) 2026-07-10 19:12:35 -07:00
Teknium
5ba2d167ba
Revert "fix(agent): release pool FDs on owning-thread client close (#61979)" (#62141)
This reverts commit cd7a8dfde0.
2026-07-10 19:12:32 -07:00
ethernet
5e849942c3 feat(dev): add isolated sandbox script for local dev
scripts/desktop-sandbox.sh runs a Hermes desktop instance in an isolated
sandbox — separate HERMES_HOME, separate Electron userData, and a
distinct
app name (HERMES_DESKTOP_APP_NAME) so it doesn't compete with the main
desktop instance's single-instance lock.

Two modes:
- Ephemeral (default): temp dir, cleaned up on exit
- --persistent: stored under .hermes-sandbox/ in the worktree git root,
  survives restarts for repeat testing

In the Nix devShell the script is available as 'sandbox'.

Also makes APP_NAME overridable via HERMES_DESKTOP_APP_NAME in main.ts —
app.setName() runs before requestSingleInstanceLock(), so the overridden
name changes the lock key. collectRelaunchEnv already preserves
HERMES_DESKTOP_* vars through self-update relaunches; test updated to
cover the new env var.
2026-07-10 18:51:49 -04:00
brooklyn!
291eae63b7
Merge pull request #62016 from NousResearch/bb/desktop-vibe-hearts
feat: vibe reactions — floating hearts on affection, across CLI/TUI/desktop
2026-07-10 16:14:27 -05:00
Brooklyn Nicholson
1fa3886bce chore(desktop): remove the DEV Shift+H heart preview
The real trigger (core `reaction` event on affectionate messages) is live, so
drop the dev-only hotkey and its always-mounted listener.
2026-07-10 16:09:23 -05:00
ethernet
fc977f62bc fix(desktop): remove old .js files
we built .ts into .js for a minute there and dumped em in src, and those
old .js files are getting resolved over top of the ts updated ones so
desktop clients don't update.

just --clean the old files so there's never a conflict :3
2026-07-10 17:05:08 -04:00
ethernet
f7c9feb395 fix(desktop): only show slash popover when / is first char
The SLASH_TRIGGER_RE regex used (?:^|[\s]) as its left anchor, so typing
a / anywhere in the message (e.g. "hello /") opened the slash command
popover — even though slash commands only execute at the beginning of a
message. Anchor the regex strictly at position 0 (^) so the popover only
appears when / is the first character, matching the actual execution
semantics. The @-mention trigger is left untouched since those work
anywhere in the text.
2026-07-10 16:14:50 -04:00
ethernet
b8880f1245 fix(desktop): type-check electron/ in CI typecheck
removing tsc -b from the build script (previous commit) also removed
the only step that type-checked the electron/ directory — the CI
typecheck job runs tsc -p . --noEmit, which uses tsconfig.json whose
include is only ["src", "../shared/src"], so electron/ was silently
uncovered. extend the typecheck script to also run against
tsconfig.electron.json so electron/ stays type-checked in CI.
2026-07-10 14:06:51 -04:00
ethernet
db8772a062 fix(desktop): don't emit js files when we build desktop 2026-07-10 14:06:51 -04:00
Teknium
b9b463f3bd
feat(security): expose deterministic tool output risk (#61793)
* feat(security): expose deterministic tool output risk

* fix(security): emit output-risk events only for findings
2026-07-10 07:58:12 -07:00
Teknium
35d777df07 chore: map WilsonKinyua release attribution 2026-07-10 07:47:10 -07:00
Wilson Kinyua
1a2f3aea9a fix tui finalize persist drop conversation_history so disconnect saves chat
finalize passed conversation_history=history aliasing the snapshot so flush
skipped every message and wrote nothing. now flush _session_messages via
marker dedup like gateway shutdown. add real db e2e tests.
2026-07-10 07:47:10 -07:00
giggling-ginger
cd7a8dfde0 fix(agent): release pool FDs on owning-thread client close (#61979)
force_close_tcp_sockets stayed shutdown-only after #29507 to avoid
cross-thread FD recycle. That left CLOSED sockets unreclaimed when
httpx.close() skipped already-shutdown sockets under long-lived
gateways (~1 CLOSED fd / 6 min via proxy).

Add release_fds= for the owning-thread dispose path only; abort still
defaults to shutdown-only.
2026-07-10 07:28:39 -07:00
PRATHAMESH75
9b72995a1d fix(cron): never stale-remove a one-shot whose run is still alive
get_due_jobs()'s one-shot stale-entry recovery (#38758) treated an
expired run_claim (#59229) as proof the claiming tick died, but a run
stalled on network I/O — or a laptop asleep mid-run — legitimately
outlives the TTL while very much alive. The recovery then deleted the
job record mid-flight: list showed the job gone, and when the run
finished mark_job_run() found nothing to update, so last_run_at /
last_status / last_delivery_error were never recorded.

Two guards, per the liveness signals available:

- Same process (the common single-gateway case): before removing a
  dispatch-limit-reached one-shot, consult the scheduler's running set
  via a lazy import; if the job is still running here it is slow, not
  stale — keep the entry.
- Cross process: run_job's monitor loop now refreshes run_claim.at
  every 60s while the run is alive (including under
  HERMES_CRON_TIMEOUT=0, which previously blocked without polling), so
  an expired claim really does mean the owner died and the TTL stays a
  dead-owner detector.

Fixes #62002
2026-07-10 07:28:33 -07:00
kshitijk4poor
90bd5b0f9b test(telegram): mirror PTB errors in heartbeat recovery 2026-07-10 19:28:03 +05:30
liuhao1024
97fb9e1f62 fix(telegram): classify PTB heartbeat transport errors 2026-07-10 19:28:03 +05:30
kshitijk4poor
54e1864577 fix(acp): unwrap web extract object titles 2026-07-10 19:14:06 +05:30
kshitijk4poor
0b753d8918 fix(display): harden fallback label formatting 2026-07-10 19:14:06 +05:30
kshitijk4poor
c2a40b2dc9 fix(web): handle short extract provider results 2026-07-10 19:14:06 +05:30
kshitijk4poor
459cf3402b fix(web): preserve extract result input order 2026-07-10 19:14:06 +05:30
kshitijk4poor
e640bb5e18 test(web): cover model-facing dict URL dispatch 2026-07-10 19:14:06 +05:30
kshitijk4poor
de33c2413b fix(web): harden extract input and display boundaries 2026-07-10 19:14:06 +05:30
liuhao1024
7ae9faecf7 fix(tools): handle dict URLs in web_extract display and tool processing
When web_search results are passed directly to web_extract, the URLs
field contains dict objects (e.g., {"url": "...", "title": "..."})
rather than plain URL strings. Two code paths assumed URLs were always
strings and crashed:

- agent/display.py get_cute_tool_message for web_extract: tried to call
  url.replace() on a dict, causing AttributeError
- tools/web_tools.py web_extract_tool loop: tried regex search on a dict,
  causing TypeError

Both now extract the URL string from dict objects (url or href field) or
fall back to empty string, preserving the cosmetic display and allowing
the tool to process the URLs correctly.

Fixes #61693
2026-07-10 19:14:06 +05:30
kshitijk4poor
8727e67295 fix(runtime): preserve resolved fork metadata
Some checks are pending
CI / Detect affected areas (push) Waiting to run
CI / Python tests (push) Blocked by required conditions
CI / Python lints (push) Blocked by required conditions
CI / TypeScript (push) Blocked by required conditions
CI / Docs Site (push) Blocked by required conditions
CI / Deny unrelated histories (push) Blocked by required conditions
CI / Check contributors (push) Blocked by required conditions
CI / Check uv.lock (push) Blocked by required conditions
CI / Lint Docker scripts (push) Blocked by required conditions
CI / Build&Test Docker image (push) Blocked by required conditions
CI / Supply-chain scan (push) Blocked by required conditions
CI / OSV scan (push) Waiting to run
CI / All required checks pass (push) Blocked by required conditions
CI / CI timing report (push) Blocked by required conditions
Deploy Site / deploy-vercel (push) Waiting to run
Deploy Site / deploy-docs (push) Waiting to run
2026-07-10 18:50:28 +05:30
kshitijk4poor
97e9c64664 fix(runtime): preserve resolved fork metadata 2026-07-10 18:32:32 +05:30
infinitycrew39
f39c88befb test(curator): assert review fork forwards pool and overrides
Regression test that _run_llm_review passes credential_pool and request_overrides from resolve_runtime_provider into the curator AIAgent fork.
2026-07-10 18:32:32 +05:30
infinitycrew39
304cdbdc77 fix(curator): forward credential pool from runtime resolution
Curator review forks now pass credential_pool and request_overrides from resolve_runtime_provider into AIAgent so pool-backed custom providers can rotate credentials on 401 like main chat.
2026-07-10 18:32:32 +05:30
kshitijk4poor
d37090ac36 test(tui): cover profile-local MCP discovery 2026-07-10 18:09:17 +05:30
HexLab98
623165a640 fix(tui): discover MCP tools in slash workers 2026-07-10 18:09:17 +05:30
gigakun3030
dfdc3156fb
fix(models): remove unavailable OpenCode Zen free models (#61163) 2026-07-10 05:25:50 -07:00
kshitijk4poor
8fa0d8bbbb test(auth): pin runtime routing persistence on failure 2026-07-10 17:50:43 +05:30
kshitijk4poor
0e67c7231d fix(auth): validate and persist shared Nous routing 2026-07-10 17:50:43 +05:30
kshitijk4poor
03b8a00e26 fix(auth): recompute Nous routing after shared recovery 2026-07-10 17:50:43 +05:30
Sami Rusani
ca6513542d fix(auth): recover runtime Nous token from shared store 2026-07-10 17:50:43 +05:30
Brooklyn Nicholson
3aaf7e3876 feat(desktop): TikTok-style vibe hearts on a reusable particle system
Add a glyph-agnostic ParticleField (float-up + organic sway/bank + springy
pop-in), skinned as pink pixel hearts. Hearts play on the pet when one is out
(in-window or popped out) and celebrate alongside; otherwise they rise from the
composer. A generic $petReaction bus mirrors the burst to the pop-out overlay
window so it reacts even while the app is minimized.

Consume the core `reaction` event to fire hearts on affectionate messages. DEV
Shift+H previews a burst.
2026-07-10 05:42:14 -05:00
Brooklyn Nicholson
fbefb5c075 refactor(tui): drive the vibe heart from the core reaction event
Replace the client-side GOOD_VIBES_RE detection with the backend `reaction`
event: on it, flash the status-bar heart and the pet's celebrate pose. Detection
now lives once in the core, so the TUI, CLI, and desktop stay in sync.
2026-07-10 05:42:08 -05:00
Brooklyn Nicholson
0e2adf9dad feat(gateway,cli): emit + consume the reaction signal
tui_gateway forwards reaction_callback as a `reaction` event (shared by the TUI
and the desktop app). The interactive CLI wires reaction_callback to flash the
pet's celebrate ("jump") pose — the CLI's analogue of hearts.
2026-07-10 05:42:04 -05:00