Repository navigation
chore(sync): merge upstream main through 8e85a0fd - #204
Conversation
૮ >ﻌ< ა ci reviewran on 0f2e4cd — fix(stt): convert segmenter failures into a transcription er
|
| Package | Before | After |
|---|---|---|
| ➕ shell-quote (nested under launch-editor) | — | 1.11.0 |
| ➖ shell-quote | 1.12.0 |
— |
How to fix:
Add the ci-reviewed label after verifying the version changes are expected.
⚠️ Warnings
CI timings · View report · View job
Wall time 62m4s vs 22m29s (+176.1%). 39 job(s) slower, 32 faster, 5 unchanged.
- Python tests / e2e-upgrade (git): +1699.0s
- Windows-only tests (advisory) / Windows-only tests: +508.0s
- Desktop update E2E / Desktop install/update E2E (Linux, build-fail): +388.0s
- JS & TS checks / JS & TS checks shard 2/5: +321.0s
- Python tests / e2e: +241.0s
ℹ️ Info
CI-sensitive file review · View job
PR touches sensitive files, but the ci-reviewed label has been added, approving them.
Sensitive files changed:
.github/workflows/ci.yaml.github/workflows/docs-site-checks.yml.github/workflows/e2e-desktop-update.yml.github/workflows/install-e2e.yml.github/workflows/js-tests.yml.github/workflows/lint.yml.github/workflows/nix.yml.github/workflows/plugin-catalog-ci.yml.github/workflows/pm-bundle.yml.github/workflows/termux-verify.yml.github/workflows/tests-os.yml.github/workflows/tests.yml.github/workflows/windows-install-update-e2e.yml.github/workflows/windows-venv-e2e.yml
…robe (NousResearch#130101) The stash-restore health check compared two independent critical-module import probes and rejected the restore on any difference, even when the stash restored no Python at all. The probe's outcome also moves with install state between the two runs (launch preparation relaunching under the live update surfaces as SystemExit(0); a dependency sync triggered by the first probe), so a docs-only stash was rolled back, the stash parked, the gateway restart skipped and the update exited 1. Only compare imports when the restore touched Python (the same restored-Python set the syntax check already uses). A restored .py that breaks an import is still rejected.
The NousResearch#130101 exemption skipped the post-restore import comparison whenever the stash restored no .py file. A restored native extension (toolsets.so shadowing toolsets.py), bytecode, a .pth or a data file read at import is not .py, so such a restore was accepted, the stash dropped, and run_agent, model_tools and toolsets were left failing to import. Skip the comparison only when every path the stash restores (read from the stash commit: worktree, index and untracked trees, renames split) has a closed non-runtime suffix (docs, images), is not a symlink, and lives in a directory HEAD already tracks. Anything else is compared as before. A docs-only restore still succeeds.
The installation page said "Light is a remote-only build variant" next to the download instructions, which read as if a remote-only Desktop client can be downloaded. Light exists only as a build target with no release leg, and there are no plans to publish it. Replace the sentence with how remote use actually works today (install a package, connect from Settings -> Gateways), drop "Light packages" from the uninstall note, and mark the variant as unpublished in the desktop README.
interrupt() derived the published tool-interrupt reason from whether a message was attached, so on the soft path a system producer had no way to label itself: passing a message booked the stop as "user sent a new message", passing none booked it as "user interrupt". Both live in USER_INTERRUPT_REASONS, so interrupt_issuer() returned None and the turn exit reason became interrupted_by_user — a system abort recorded as a human stop. An explicit tool_reason now wins on the soft path as it already did for hard_cancel, keeping the message heuristic only as the fallback. (cherry picked from commit 495f1a8)
The sequential-batch timeout guard stopped the turn with a bare interrupt(message), which the soft-path heuristic read as "user sent a new message". The abort was therefore recorded as interrupted_by_user and the skipped calls told the user they had sent a message they never wrote. Pass the guard's own tool_reason so the exit reason names the real issuer and the skipped-call notice can describe it. (cherry picked from commit a756f78)
Both sequential skip sites hardcoded a user-initiated cause ("User sent a new
message" / "skipped due to user interrupt"), so any system abort that reached
them asserted an action the user never took.
Render the notice from the published reason instead: a system issuer describes
itself, and only the genuinely user-initiated reasons keep the user-facing
wording.
(cherry picked from commit 8efe429)
close_interrupted_tool_sequence() appends a synthetic assistant turn when an interrupted tail is a raw tool result, so the next user message does not land as tool -> user. The row carried a user-visible "Operation interrupted." string and was persisted as an ordinary assistant message, so background preemption — session reaping, watchdogs, batch guards — surfaced in the transcript as an interruption the user was expected to act on. Store it in the same hidden shape turn_api_call.py already uses for interrupt placeholders: content="" plus display_kind="hidden" keeps it out of rendered transcripts, while the api_content sidecar carries the LLM-visible text and is substituted at API-build time. The alternation guarantee is unchanged. (cherry picked from commit 22502b1)
The exit reason and the skipped-call notice must both name the real issuer: a system producer that stops the turn softly labels itself via tool_reason, while a message-carrying soft interrupt with no reason stays a human stop. The wording derives from the recorded reason, so a system abort never tells the user they did something they did not. (cherry picked from commit ec65b95)
The sequential path was converted to interrupt_skip_wording() but the
concurrent batch kept three hardcoded user-blaming strings: the pre-batch
skip (execute_tool_calls_concurrent), the unfilled-slot notice
(_unfinished_tool_result) and emit_cancelled's hook message. A system
producer that stops a parallel batch — watchdog, lease loss, SSE
disconnect, or any segment routed through execute_tool_calls_segmented —
still told the model "skipped due to user interrupt" while
interrupt_issuer() booked the same turn as interrupted_by_system. Render
all three from the recorded reason like the sequential siblings; the
observability enums (error_type/hook_error_type) stay untouched.
Also harden the interpolation the wording rides on: _append_skipped_tool_results
substitutes {name} with str.replace instead of str.format, and
interrupt_skip_wording escapes braces in caller-supplied reasons, so a
free-form tool_reason can never raise KeyError/IndexError mid-turn.
(cherry picked from commit b785bcc)
close_interrupted_tool_sequence hid every synthetic closing turn behind
display_kind="hidden", but four of its five callers pass a real user-facing
notice — the truncation terminal ("Response truncated — …"), the context-
overflow partial text, tool-validation partials and abort_turn_on_interrupt's
"Operation interrupted: …" lines. Hiding those removed the turn's only
explanation from every transcript surface.
Narrow the hidden shape to what it was for: an empty final_response or the
bare interrupt placeholder (which previously rendered as the "Operation
interrupted." bubble users never asked for). Any other text is rendered
verbatim as before. Alternation safety is unchanged in both branches.
(cherry picked from commit a637af2)
A hidden assistant row is hidden from the transcript, not from the request: the projection substitutes its api_content sidecar into content on every call. The value chosen for that slot in NousResearch#88955 was "[response interrupted]" - a short natural-language phrase sitting in the model's own prior-turn position, which models reproduce verbatim: an ordinary instruction answered with clean finish_reason=stop and nothing but the placeholder (NousResearch#132949). The same hazard was already recognised twice in this codebase - the redirect scaffold is barred from assistant text because "the model echoes it" (NousResearch#81841), and a runaway repetition partial is replaced by a self-describing label rather than replayed (NousResearch#112764) - but the placeholder was never given the same treatment. Change the placeholder to a structural label the model has no reason to say, and retire rows already persisted under the pre-change spellings: the prep filter that dropped scaffold ghosts now also drops hidden rows carrying the legacy placeholder or the scaffold marker. Dropping is not always safe. A hidden close appended after a tool result is the only thing separating that tool message from the next user turn; removing it recreates the tool -> user alternation violation that makes strict providers ignore prior context (NousResearch#48879) - the exact failure the close exists to prevent, and one repair_message_sequence deliberately leaves alone. When the neighbours would form that pair, rewrite the row's text instead of dropping it. The rewrite targets the current placeholder, so a neutralised row never re-triggers the filter. (cherry picked from commit 227e9a1)
The retirement filter gets five cases: a legacy placeholder row between two user turns is dropped; the same row after a tool result is kept with its text rewritten (dropping it would hand the provider the tool -> user pair that close_interrupted_tool_sequence exists to prevent); a row carrying the current placeholder is left untouched, which is what keeps NousResearch#88955's re-heal guard intact and proves the rewrite cannot re-trigger itself; a scaffold ghost still drops as before; and a visible caller banner is never treated as a ghost. Verified discriminating: reverting the filter to the scaffold-only version fails all five, restoring it passes. The existing NousResearch#88955 assertions compared against the literal "[response interrupted]" in four places. That couples the tests to one chosen spelling - any wording change, including this one, breaks them for the wrong reason. They now compare against the constant, asserting what the contract actually says: the durable row stays empty and hidden, the wire copy carries the neutral payload the writer stamped, and neither is ever the interrupt scaffold (NousResearch#81841). (cherry picked from commit e4ebf01)
The terminal batch-timeout guard still passed "terminal batch tool did not complete" as the interrupt message. That text lands in _interrupt_message, which the gateway (_run_agent_drain_pending) and the CLI (_chat_resolve_interrupt) re-queue as the user's next turn, so a guard abort injected a fake user message. Call interrupt(tool_reason=...) with no message, and use a slug-safe reason so the issuer reads interrupted_by_system(terminal_batch_timeout) rather than terminal_batch_aborted:_call_timed_out. Message-less guard shape from NousResearch#130209. Co-authored-by: Yuan Li <dskwelmcy@163.com>
Skipped-call notices now render from _tool_interrupt_reason instead of a hardcoded "skipped due to user interrupt". The concurrent pre-flight stub never recorded a reason, so it fell to the "Turn interrupted" fallback and the stale assertion failed; record the new-message reason the real soft path publishes and assert its wording. The ACP fixture is cosmetic but should show the shape the executor actually emits.
_neutralise_replay_echo_ghosts rewrote the caller's list in place (seq[:] = out) and overwrote content/api_content on durable row dicts. Those dicts are the live session rows the SessionDB flush cursor and _DB_PERSISTED_MARKER track, so an in-place edit silently diverges the in-memory row from what state.db holds. BASE built a new list here; keep that contract: return a filtered list and neutralise a shallow copy of the row, which only shapes this request's replay.
Keep one attribution test that goes red on BASE (a tool_reason soft interrupt is booked to the system issuer with no requeued message) and one replay-echo test (a legacy placeholder row next to a tool tail is neutralised on a copy, never dropped into tool -> user, and the durable row is left untouched). The remaining wording/brace/close-row cases pin implementation detail already covered by the updated finalizer, steer and concurrent-interrupt tests.
Fold finding 3 (B02 gate): interrupt_skip_wording doubled braces on the
assumption the wording reaches str.format, but every consumer substitutes
{name} with str.replace, so a reason like "a{b}" rendered as "a{{b}}".
Drop the escape and its docstring claim, and look user-stop wording up
directly in _USER_STOP_WORDING.
Fold finding 2 (B02 gate, sibling of NousResearch#130207): both KeyboardInterrupt handlers in tool_executor call agent.interrupt("keyboard interrupt"). With a message and no tool_reason, interrupt() records "user sent a new message", so the skipped-call notices for a Ctrl-C claimed the user had sent a new message. Pass tool_reason=_REASON_USER_INTERRUPT so the recorded reason (and the rendered wording) is "User interrupt"; the issuer stays None, so attribution to the human is unchanged.
There was a problem hiding this comment.
💡 Codex Review
hermes-agent/hermes_cli/web_routers/files.py
Lines 397 to 398 in 103dd0c
When an allowlisted CDN URL returns a very large response, AsyncClient.get() buffers the entire body before returning, so the 25 MB check at lines 407–409 occurs only after the gateway has already allocated the unbounded payload. A malformed or unexpectedly large image can therefore exhaust the gateway's memory despite the advertised cap; stream the response and stop reading once _MEDIA_MAX_BYTES is exceeded.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ments Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5188bd40c8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…stream on bad frames Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…it gate Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: deefe2c4d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ctation buffer overflow Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c2825f70c9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…bprocess Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eck) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6741e9408
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… scopes Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 134c047101
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e13300f73b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…rt outranks legacy field Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… thread Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b23bbdea98
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1867d967d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
What does this PR do?
Merge the reviewed
NousResearch/hermes-agentmain snapshot into the publicmrkillbob/hermes-agentfork while retaining fork security, routing, companion and local-model behavior. Both actual default branches aremain. Initial inspection found no duplicate open sync PR and a divergence of 1,778 fork commits ahead / 1,682 upstream commits behind.23ed0acc3f8e9d72c67c663dcefc642ca5682c598e85a0fd8cf813336b4c24c1eb68d1b25fb11a26f37e783fcd835cf432e5e7568f3b17ea8e8977e5819cc3cbe02104c420ea32f1f1a924247d42eaf8Latest observed upstream main is
a50406d9b7474b060450d2dcaff8743c977d296a, 170 commits beyond the reviewed snapshot. Those later commits are outside this PR. The fork main remains at the base above.Integration decisions
Independent task-local reviews covered authentication/routing, UI/gateway, plugins/tools/updater, CI/dependencies, security-sensitive repairs and final integration. Normal hooks and nonforce publication were used. Shared checkout and its dirty work were preserved. Static validation parsed 961 changed Python files, TOML and lock metadata and checked whitespace/conflicts. No local installs/runtime tests ran under the Mac capacity hold.
Exact published-head validation
Main 37596522780 attempt 1 completed with 89 successful, four failed, five skipped jobs. The authorized same-head general-E2E retry passed as attempt 2; final combined results are 90 successful, three failed, five skipped. Parallel completed runtime results were carried forward, not rerun. Website audit, maintainer label and aggregate remain red; no full-green claim.
ci-reviewed; independent agent reviews do not assert that labelRemaining security and qualification limits
Website remaining high leaves are braces (GHSA-vfj7-8cjw-p6xm) and http-cache-semantics (GHSA-ch52-4w7c-c8xp); counts include dependent findings. Braces has no published fixed release. Its Docusaurus docs glob path is reachable; this is not an exploitation claim. HTTP cache semantics 4.3.0 lies outside the recorded advisory range but leaves the reported vulnerable guards unchanged and is not age eligible; no honest fixed-source qualification established. Supported age-eligible compression 1.8.2, proxy-addr 2.0.8 and tinypool 2.1.2 fixes removed prior critical findings. No audit waiver or additional age exemption was added. Root audit qualifies a separate graph.
Michael explicitly approved narrow npm release-age exceptions on 2026-10-07 at 05:37:40 UTC: five root and two website exact locked pins. Both global age floors remain 14 days; no wildcard/lifecycle/permission changes. Remove exemptions manually after the timestamps below; comments do not expire automatically.
Desktop simple-git v4 compatibility and the actual Git abbreviated-option guard have hosted regression coverage. Lock integrity/metadata was independently verified; hosted managed install is the runtime installation receipt.
Issue #203 was closed under Michael's explicit approval using the exact oldest macOS source-transition replay on historical head
e80e073: resolution receipts. This qualifies that historical leg, not the entire installer matrix or current head. Prior attempts and failure evidence are retained in the task-local ledger. Only free standard hosted CI was used; task-owned queued self-hosted bridges were cancelled and terminal cancellation verified, including 37601806700 after attempt 1 and 37604526568 after attempt 2. No merge or deployment.