Commit graph

16597 commits

Author SHA1 Message Date
kshitij
646e71a9be fix: sanitize subprocess env for DDGS worker
os.environ.copy() passes all Hermes secrets (gateway tokens, API keys,
dashboard session tokens) into the DDGS child process. Use
_sanitize_subprocess_env() to strip Hermes-managed secrets before
spawning the worker.
2026-07-21 11:51:01 +05:30
HexLab98
77ee16b747 test(web/ddgs): cover GIL-hold timeout, interrupt, and worker reap
Regression tests for #68096: native GIL-hold and sleep hooks must time
out or interrupt promptly with no orphaned search workers.
2026-07-21 11:51:01 +05:30
HexLab98
21c7e49ad0 fix(web/ddgs): isolate DuckDuckGo search in a disposable process
ThreadPoolExecutor timeouts cannot fire when primp holds the GIL in
native code (#68096). Run each search in a child process the parent can
terminate/kill, and honor tools.interrupt between polls.
2026-07-21 11:51:01 +05:30
Gille
693d3909c8 docs(portal): remove retired Nous Chat references 2026-07-21 11:25:04 +05:30
kshitijk4poor
2da64e7840 refactor: drop platform kwarg, fix PTY test cleanup
- Remove redundant platform= test seam from _terminal_may_leak_cpr();
  use monkeypatch.setattr(sys, 'platform', ...) consistently in both
  test files.
- Wrap PTY tests in try/finally for fd cleanup on assertion failure.
- Guard select.select() in terminal thread against OSError after fd
  close (fixes PytestUnhandledThreadExceptionWarning).
- Trim PR-number reference from test module docstring.
2026-07-21 11:18:28 +05:30
HexLab98
f6d82e1267 test(cli): prove local CPR leak and Application CPR-disabled wiring
Add a delayed-CPR PTY harness (no SSH) plus selection/Application
assertions for POSIX local and Windows preserve-default. Update the
gating unit test to the new contract.
2026-07-21 11:18:28 +05:30
HexLab98
3f820a1c7c fix(cli): suppress CPR on POSIX local TTYs under load
Delayed ESC[6n replies leak as ^[[row;colR into the classic CLI on
SSH/slow PTYs (#13870) and on local POSIX TTYs under heavy subagent
load. Suppress CPR on non-Windows platforms (layout hint only); keep
native Windows on prompt_toolkit's default pending native coverage.
Wire selection through _select_classic_cli_pt_output.
2026-07-21 11:18:28 +05:30
brooklyn!
7651764ce6
Merge pull request #68390 from NousResearch/bb/paste-history-recall
fix(cli,tui): recall real paste content on up-arrow
2026-07-20 23:33:25 -05:00
Brooklyn Nicholson
79af472582 fix(cli,tui): recall real paste content on up-arrow
Large pastes collapse to a placeholder in the composer, but input history
stored the placeholder — so up-arrow recall showed a truncated reference
(CLI) or lost the content entirely (TUI, where the `[[…]]` label has no
backing snip after submit).

Store the expanded content in history instead:
- CLI: `_inline_pastes()` expands `[Pasted text #N -> file]` into the buffer
  before `reset(append_to_history=True)`; also reused by the external editor
  (dedup). History nav suppresses re-collapse of recalled content.
- TUI: `dispatchSubmission` pushes `expandSnips(pasteSnips)(full)`; idempotent
  on label-free text so re-submitting a recalled entry stays stable.
2026-07-20 23:23:40 -05:00
hermes-seaeye[bot]
477c08b447
fmt(js): npm run fix on merge (#68305)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-21 01:02:12 +00:00
xxxigm
f657840e06
fix(desktop): keep composer draft across compression tip rotation (#68079)
* fix(desktop): keep composer draft across compression tip rotation

Auto-compression swaps the live stored session id while the user may still
be typing. Scope the composer/queue key on the lineage root and migrate any
tip-keyed draft/queue entries onto that durable key when the tip rotates so
the in-progress prompt does not vanish when the response lands.

* test(desktop): cover draft survival across compression tip rotation

Add regression coverage for migrateSessionDraft, lineage-scoped composer
keys, and the rotation path that previously wiped an in-progress draft.
2026-07-20 20:55:04 -04:00
Gille
272bbaf792
fix(desktop): avoid false remote gateway reauthentication (#68250)
* fix(desktop): avoid false remote gateway reauthentication

Co-authored-by: Rod-fernandez <rodrigo@nxtlevelsaas.com>
Co-authored-by: David Andrews (LexGenius.ai) <david@lexgenius.ai>

* fix(desktop): harden remote revalidation state

---------

Co-authored-by: Rod-fernandez <rodrigo@nxtlevelsaas.com>
Co-authored-by: David Andrews (LexGenius.ai) <david@lexgenius.ai>
2026-07-20 20:54:36 -04:00
ethernet
5c4dc46cce
Merge pull request #68298 from NousResearch/ethie/cage-nix
nix: add cage to devDeps
2026-07-20 20:43:09 -04:00
ethernet
933c823ae8 nix: add cage to devDeps 2026-07-20 20:38:59 -04:00
brooklyn!
fb0ed8396c
Merge pull request #68259 from NousResearch/bb/multi-window
feat(desktop): run multiple GUI windows (New Window)
2026-07-20 18:50:42 -05:00
Brooklyn Nicholson
a41346f8ae refactor(desktop): tidy the cross-window deduper
Drop the unused DEDUPE_WINDOW_MS export and rename its interval so
"window" isn't overloaded against BrowserWindow in a multi-window
feature (windowMs → intervalMs). DRY the completion-sound play path.
No behavior change.
2026-07-20 18:36:05 -05:00
Brooklyn Nicholson
fb0c6d9ee1 fix(desktop): de-dupe cross-window cues so peers don't spam
With multiple full windows, each renderer independently reacts to the
same backend event, so one-shot cues fired N times: OS notifications
(the per-renderer throttle can't see other windows), the turn-end sound
(playCompletionSound runs on every message.complete, ungated by focus),
and auto-spoken replies (double voice when a chat is open in two windows).

Add a single race-free owner in the main process (electron/event-dedupe.ts):
main handles IPC serially, so the first window to claim a key within a
short window wins and peers stay quiet. Notifications collapse at the
hermes:notify choke point; the sound and spoken replies claim via a new
hermes:ambient:claim IPC (keyed by session / reply id). Off Electron the
claim falls back to "emit", preserving single-window behavior.

The sound's mute check runs before the claim so a muted window can't win
the cue and silence an audible peer.
2026-07-20 18:33:14 -05:00
Brooklyn Nicholson
a90ca7fe34 feat(desktop): wire New Window to ⌘⇧N + command palette
Repoint session.newWindow (⌘⇧N) from the compact new-session pop-out to
openNewWindow(), which opens a full peer instance via the new openWindow
bridge, and add a "New Window" entry to the ⌘K palette (shown with its
hotkey hint, gated on canOpenNewWindow()). Relabel the action "New window".

Drops the retired openNewSessionWindow bridge and the vestigial
isNewSessionWindow()/new=1 flag; renames the shared opener helper.
2026-07-20 18:02:48 -05:00
Brooklyn Nicholson
b586e4eff2 feat(desktop): open multiple full app windows (electron)
Add createInstanceWindow() — a full-chrome peer of the primary that
renders the complete app (sidebar, routing, its own draft) against the
shared backend, so several GUI windows can run at once. Mirrors the
primary's window options + chatWindowWebPreferences (backgroundThrottling
stays off so a streamed answer never stalls when blurred) but never
overwrites the mainWindow global and doesn't respawn the backend — the
renderer's getConnection() joins the running one. New windows cascade off
their source via the pure, tested instanceWindowBounds().

Exposed via the hermes🪟openInstance IPC and a "New Window" File
menu item. Per-window fullscreen state now targets the window itself, and
titlebar/native-theme repaints reach every open chat window instead of
only the primary.

Retires the now-orphaned compact new-session pop-out (its only caller was
⌘⇧N, repointed in the follow-up commit): drops createNewSessionWindow,
the hermes🪟openNewSession handler, and the newSession/new=1 URL
flag.
2026-07-20 18:02:44 -05:00
ethernet
a41d280f95
Merge pull request #65964 from NousResearch/ethie/ci-review-comment
ci: live-updating PR review comment with structured job statuses
2026-07-20 17:39:36 -04:00
ethernet
d57947b493
fix(desktop): bump skills test timeout to fix cold-start flake (#68235)
Test 1 in skills/index.test.tsx pays the full cold-start cost (jsdom env
init + module transform + the @/hermes/@/store/profile import graph),
which pushed past vitest's 5000ms default under load — caught at 8871ms
on one run, 6.6s pure test time on another. Tests 2-4 are ~30-130ms
each because all that setup is already cached, so only test 1 was at
risk of timing out.

Bump the describe-level timeout to 15s. Verified with 10 consecutive
runs, 4 of which took 5.5-6.6s of test time and would have hard-failed
under the old 5s default.
2026-07-20 21:27:34 +00:00
ethernet
5c7993ec60 fix(ci): add detect to all-checks-pass needs so its failure blocks merge
If detect fails, all downstream sub-workflows get SKIPPED (they have
needs: detect). all-checks-pass used if: always() and only checked the
sub-workflows — which all showed as 'skipped' (= success) — so it passed
even though the root cause (detect) failed. This made the PR mergeable
despite a broken CI pipeline.

Add detect to all-checks-pass needs so its failure propagates to the
gate job and blocks the merge.
2026-07-20 17:27:30 -04:00
ethernet
1f76bdc5b2 fix(ci): pass App secrets as inputs to composite action
Composite actions cannot access the secrets context — the runner's
template engine rejects secrets.* references at load time with
'Unrecognized named-value: secrets'.

Move APP_ID and APP_PRIVATE_KEY from direct secrets.* references inside
the composite action to inputs passed by each calling workflow. The
fallback logic (GITHUB_TOKEN when APP_ID is empty, for fork PRs) stays
in the composite action's check step.
2026-07-20 17:27:30 -04:00
ethernet
7a69b82ad4 ci: migrate AUTOFIX_BOT_PAT to GitHub App token
Replace the long-lived fine-grained PAT (AUTOFIX_BOT_PAT) with short-lived
(1-hour) installation access tokens minted via a new get-app-token composite
action wrapping actions/create-github-app-token@v3.2.0.

The PAT was used in 13 spots across 8 workflow files for gh CLI / GitHub API
calls. The per-repo GITHUB_TOKEN (1,000 req/hr) was getting rate-limited when
multiple workflows fire concurrently (deploy-site, skills-index, ci-timings,
supply-chain-audit, js-autofix). App installation tokens get 5,000 req/hr
per installation and are scoped to the App's permissions, not a user account.

New composite action: .github/actions/get-app-token/
  - Wraps actions/create-github-app-token@bcd2ba49 (v3.2.0, SHA-pinned)
  - Reads APP_ID + APP_PRIVATE_KEY repo secrets
  - Outputs a 1hr installation token via steps.app-token.outputs.token

Requires two new repo secrets (set after creating the GitHub App):
  - APP_ID: the App's numeric ID
  - APP_PRIVATE_KEY: the PEM private key

App installation permissions needed:
  contents: write    (js-autofix push, pypi release upload)
  pull-requests: write (js-autofix PR create/merge, supply-chain comment)
  issues: write       (skills-index-freshness issue creation)
  actions: write     (skills-index workflow trigger)
  workflows: write   (skills-index triggers deploy-site.yml)

The AUTOFIX_BOT_PAT secret can be deleted once CI passes on this PR.
The comment in js-autofix.yml noting that PAT pushes trigger downstream
workflows is updated — App tokens have the same property (they are not
GITHUB_TOKEN), so the concurrency-cancel loop logic is unchanged.
2026-07-20 16:48:25 -04:00
ethernet
b9f82ed39f ci: live-updating PR review comment with structured job statuses
Replace the static comment-pending + comment-results two-job pattern
with a live-updating comment system that polls the GitHub Actions API
every 15s, re-assembles the review comment from whatever results are
available, and upserts it via the <!-- hermes-ci-review-bot --> marker.
The comment updates in real time as each job finishes — no waiting for
the full pipeline.

Every CI job that wants to appear in the review comment emits a
review_status output — a JSON array of objects, each with a source
and a results array:

    [
      {
        "source": "review-label-gate",
        "results": [
          {"kind": "action_required", "title": "...", "summary": "...",
           "how_to_fix": "..."},
          {"kind": "info", "title": "...", "summary": "..."}
        ]
      },
      {
        "source": "ci timing",
        "results": [
          {"kind": "warning", "title": "CI timings", "summary": "...",
           "detail": "...", "link": "..."}
        ]
      }
    ]

One job can emit multiple results of different kinds. The source field
is used to exclude the corresponding job from the synthesized error
list (case-insensitive, hyphen-normalized matching against GitHub
Actions job display names).

| job                        | source                   | kind (on failure)         | section              |
|----------------------------|--------------------------|---------------------------|----------------------|
| review-labels              | review label gate        | action_required / info    | Action required      |
| lockfile-diff              | lockfile-diff            | action_required           | Action required      |
| ci-timings                 | ci timing                | warning / info            | Warnings             |
| supply-chain scan          | supply chain             | error / (none)            | Job failures         |
| supply-chain dep-bounds    | supply chain             | action_required / (none)  | Action required      |
| osv-scanner                | osv scan                 | warning / (none)          | Warnings             |
| uv-lockfile-check          | uv.lock check            | action_required / (none)  | Action required      |
| history-check              | unrelated histories      | action_required           | Action required      |
| contributor-check          | contributor attribution  | action_required           | Action required      |

Jobs that find nothing emit [] (empty array) — no noise info items.

A single comment-live job polls the GitHub Actions API every 15s,
classifies jobs into (completed, pending), assembles the comment, and
upserts it. Merges review_status outputs from all needs jobs via
toJSON(needs.*.outputs.review_status), and downloads the ci-timings
artifact when it becomes available. Shows commit SHA + message below
the header.

The assembler has ZERO job-specific knowledge. It just:
1. collect_from_statuses() — flattens all nested status objects into ReviewItems
2. collect_failed_jobs() — synthesizes errors for failed jobs with no declared status
3. _attach_job_urls() — fills in per-job log links for ALL items
4. render_comment() — groups by severity, renders with group headers

Each item shows links inline next to the title: View report (job-emitted
URL) and View job (auto-attached logs link). Each info item is its own
collapsible <details> block.

    # ૮ >ﻌ< ა ci review

    running on abc1234 — commit message first line

    ##  Job failures
    ### {title} · [View job](url)
    {summary}

    ## ⚠️ Action required
    ### {title} · [View job](url)
    {summary}
    **How to fix:**
    {how_to_fix}

    ## ⚠️ Warnings
    ### {title} · [View report](url) · [View job](url)
    {summary}
    {detail}

    <details><summary>{title}</summary>
    {content}
    </details>

    Still running 3 jobs: ci-timings, docker

- test_assemble_review_comment.py (48 tests): collect_from_statuses,
  collect_failed_jobs with exclude_sources, _attach_job_urls,
  render_comment (group headers, inline links, commit info, per-item
  details, pending footer), assemble integration
- test_live_comment.py (16 tests): classify_jobs pure function
- test_timings_report.py (10 tests): generate_review_status nested format
- test_lockfile_diff.py (6 tests)
- test_classify_changes.py (32 tests, pre-existing)
2026-07-20 16:48:25 -04:00
Gille
d7b36070ef
fix(checkpoints): honor gateway config and task cwd (#68195)
* fix(gateway): wire checkpoint config into agents

* fix(checkpoints): resolve gateway file paths by task cwd
2026-07-20 13:04:12 -07:00
ethernet
e2fd8a37dc
fix(desktop): refresh repo status on session switch with unchanged cwd (#68208)
fix(desktop): refresh repo status on session switch with unchanged cwd
2026-07-20 20:01:28 +00:00
brooklyn!
67e73ae958
Merge pull request #68140 from NousResearch/bb/desktop-keep-awake
feat(desktop): keep-computer-awake toggle
2026-07-20 14:29:04 -05:00
ethernet
6fbb4cea00
Merge pull request #65805 from NousResearch/ethie/e2e
Desktop E2E: Playwright suite with visual regression diffs
2026-07-20 15:19:05 -04:00
Brooklyn Nicholson
e0028410ee Merge remote-tracking branch 'origin/main' into bb/desktop-keep-awake
# Conflicts:
#	apps/desktop/src/app/settings/config-settings.tsx
2026-07-20 14:14:13 -05:00
Brooklyn Nicholson
3ef5249558 refactor(desktop): drop keep-awake statusbar toggle; persist in main
Keep-awake lives only in Settings → Advanced now. Remove the statusbar
quick-toggle (+ its Sun icon, store toggle helper, and keepAwakeOn/Off
strings across locales). Since the statusbar was what eagerly loaded the
store at boot, move persistence to the main process (keep-awake.json,
re-applied on app ready — same pattern as translucency), so a cold launch
restores the blocker without the renderer opening Settings.
2026-07-20 13:57:41 -05:00
Brooklyn Nicholson
fc8e96b200 fix(desktop): vertically center settings panel loader
The settings OverlayMain has a titlebar-height top pad (no bottom pad), so
the full-panel LoadingState centered in the band beneath it and read low.
Cancel the top pad on the loader so it centers in the whole card; the one
inline (mid-panel) memory loader switches to a plain min-height PageLoader
so it's unaffected.
2026-07-20 13:49:27 -05:00
ethernet
3640b8e666 ci(windows): pull e2e-windows scaffolding out to its own branch
The Windows installer E2E scaffolding (e2e-windows.yml + AutoHotkey
helper + button screenshots) lands in its own draft PR (ethie/windows-e2e)
targeting this branch, so it can be reviewed + iterated independently of
the desktop E2E suite. Both jobs remain `if: false` until the installer
E2E is ready to run.
2026-07-20 14:45:24 -04:00
Brooklyn Nicholson
ac9a1014a6 refactor(desktop): drop System settings section; keep-awake → Advanced
Revert the dedicated System section: Window Translucency + UI Scale move
back to Appearance, and Haptics returns to its titlebar-only home. Keep
computer awake now lives as a device-local toggle at the top of Advanced
(a ConfigSettings section-specific extra, like the Model block), keeping
the statusbar quick-toggle. Relocated i18n back to settings.appearance /
settings.config across all four locales.
2026-07-20 13:40:16 -05:00
ethernet
2e10d7b942 fix(desktop): address review — overlay a11y, e2e typecheck, nits
Blocking #1 — gateway-connecting-overlay.tsx reduced-motion regression:
the top `if (reduce) setPhase('gone')` fired unconditionally on mount
whenever reduce-motion was on, so every OS reduced-motion user lost the
CONNECTING overlay during cold boot entirely (jumped to 'gone' before the
gateway was even open). The intent was to skip the exit *choreography*,
not to skip showing the overlay. Removed the unconditional top block and
the redundant nested preview block; kept only the third branch
(`gatewayState === 'open' && shownRef.current` → `reduce ? 'gone' :
'text-out'`) which correctly gates the short-circuit on connect. Also
fixed `if(reduce)` missing-space, 6-space misindent, and the same 3-line
comment pasted three times.

Nit #1 — tsconfig excludes e2e, so specs were never typechecked in CI.
Added tsconfig.e2e.json (extends base, includes e2e/ + playwright.config.ts,
adds @playwright/test types) and wired it into the typecheck script. This
surfaced three latent type errors that are fixed in the same commit:
  - fix-electron-tracing.ts: `app._context` and `electron._playwright` are
    private APIs — added `as any` on the access before the existing cast.
  - playwright.config.ts: `reducedMotion: 'reduce'` directly under `use:`
    is not a valid UseOptions property in playwright 1.58; it's a
    BrowserContextOption accessed via `contextOptions: { reducedMotion:
    'reduce' }`. The old form was silently ignored at runtime, so
    reduced-motion emulation wasn't actually active — screenshots could
    catch overlays mid-fade (exactly what the comment warned about).

Nit #2 — fix-electron-tracing.ts reaches into Playwright internals
(_playwright, _allContexts, _context) with no public contract. Added a
header comment calling out the `@playwright/test` exact pin (=1.58.2) so a
future bump knows to re-verify the private symbols still exist.

Nit #3 — main.ts TEST_WORKER_INDEX block had stray 6-space indentation.

Verified: tsc -p . && tsconfig.electron && tsconfig.e2e → 0 errors;
vitest boot-failure-overlay (3/3) + boot-failure-reauth (21/21) pass;
npm run build clean; playwright e2e/boot-failure.spec.ts 2/2 pass.
2026-07-20 14:37:39 -04:00
ethernet
0b40ba10cd revert(installer): drop install.ps1 rewrite from E2E branch
Pulls commit 3dab86a95 out of this branch per review — the install.ps1
rewrite (swapping the astral install-script for a direct GitHub-zip
download) is a real Windows-installer behavior change that belongs in
its own installer PR, not riding along in a Desktop E2E PR.

e2e-windows.yml is `if: false` on both jobs and can't run on Linux CI,
so the rewrite lands here with no coverage. The deleted install tests
(test_install_ps1_native_stderr_eap.py,
test_install_ps1_uv_powershell_host.py) are restored — they'll be dropped
alongside the installer change in its own PR.

Original commit 3dab86a95 will be cherry-picked onto a dedicated
installer PR.
2026-07-20 14:35:22 -04:00
Teknium
3ef6bbd201
chore: release v0.19.0 (2026.7.20) (#68175) 2026-07-20 11:35:21 -07:00
ethernet
33c154e41f revert(desktop): restore Preparing error state in onboarding
Reverts the Preparing component changes from b2857110b so the progress bar
turns red (bg-destructive) and the error text shows below it when boot.error
is set, instead of bailing out with an early return null.

The corresponding e2e guard in waitForBootFailure (e2e/fixtures.ts) that
rejected any progress bar in the DOM is dropped — it now waits for the
failure dialog (Retry/Repair/Use local gateway/Connection settings) or the
"Desktop boot failed" toast. The boot-failure.spec.ts header comment is
updated to match.

Verified: tsc clean, vitest boot-failure-reauth (21/21) + boot-failure-overlay
(3/3) pass, npm run build clean, playwright e2e/boot-failure.spec.ts 2/2 pass.
2026-07-20 14:32:13 -04:00
ethernet
18ca0e862c
Merge pull request #66471 from NousResearch/ethie/typescript-lsp
fix(lsp): never report stale diagnostics — version-gated freshness for slow servers
2026-07-20 14:16:50 -04:00
Teknium
0e281b58e6 fix(matrix): class-level split-threshold defaults for partially-constructed adapters
Text-batching tests (and any tooling) build MatrixAdapter via
object.__new__ without running __init__; moving _split_threshold from a
class constant to an instance attribute made _flush_text_batch die with
AttributeError, silently dropping the flush. Restore class-level
defaults (max_message_length, _split_threshold) that __init__ overrides,
and derive the near-limit test payload from adapter._split_threshold
instead of the old hardcoded 3950.
2026-07-20 11:10:49 -07:00
Teknium
086a56a028 fix(matrix): correct platform hint over-claims, add regression tests + docs
Follow-up to the salvaged #52552 and #53083 commits:

- Rework the Matrix PLATFORM_HINTS entry around what the adapter actually
  emits: headings, numbered lists, blockquotes, strikethrough-free markdown
  all render (the adapter converts them to sanctioned HTML). Keep the
  genuinely valuable guidance: no Markdown tables (Element X / Beeper /
  mobile clients don't render HTML tables — cells collapse into one line),
  no spoilers/checkboxes/~~strikethrough~~ (not converted by
  python-markdown), prefer descriptive link text.
- Regression test: hint must steer models away from tables.
- Fix test_long_response_split_preserves_thread_context to derive its
  payload size from the adapter's configurable limit instead of assuming
  the old hardcoded 4000.
- Document matrix.max_message_length in the Matrix docs page.
- Contributor mapping for RKelln.
2026-07-20 11:10:49 -07:00
nankingjing
35e0f56fbf fix(matrix): make outbound message length configurable (#53026)
Raise the Matrix adapter default chunk size from 4,000 to 16,000
characters and allow overrides via config.yaml or MATRIX_MAX_MESSAGE_LENGTH.

Fixes #53026
2026-07-20 11:10:49 -07:00
Ryan Kelln
08626c18be fix(prompt_builder): improve Matrix PLATFORM_HINTS with tested formatting rules
- Replace outdated brief hint with comprehensive, tested rules
- Document exactly what renders on Matrix and what does not
- Add critical linebreak semantics (two trailing spaces = soft break)
- Add link formatting guidance (descriptive text, never bare URLs)
2026-07-20 11:10:49 -07:00
Teknium
a5b9803a51 test(transports): assert Moonshot wire schema carries required:[]
Transport-level regression for #66835 — verifies the outgoing tool
schema at the chat_completions build_kwargs boundary, not just the
sanitizer unit.
2026-07-20 11:05:52 -07:00
PRATHAMESH75
acc1b6e76a fix(agent): inject empty required array on Moonshot object schemas
Moonshot/Kimi's tool-parameter validator rejects object schemas that omit
the required key with HTTP 400 ("required must be an array"), even though
standard JSON Schema allows omitting it. Any Hermes tool with zero required
parameters (browser_back, delegate_task, project_list, several MCP list_*
tools, etc.) tripped this when routed to a Moonshot endpoint.

Add a Rule 4 to the sanitizer: every object schema gets a required array,
defaulting to []. Existing lists are preserved but pruned to names that
actually appear in properties (dangling entries are also rejected upstream).
Applied recursively to nested object schemas and to the coerced/empty
top-level fallback.

Fixes #66835
2026-07-20 11:05:52 -07:00
ethernet
eb09df6ec8 refactor(lsp): version-tagged _DocState replaces timestamp freshness tracking
The staleness fix (f9b1fd799) bolted two wall-clock dicts (_changed_at,
_pulled_at) onto a client that already scattered per-document state
across six parallel dicts (_files, _push_diagnostics, _pull_diagnostics,
_published, _published_version, _first_push_seen) — eight maps kept in
sync by hand.

Collapse all of it into one _DocState per path, and use the LSP document
version as the freshness token instead of clocks:

- didChange bumps doc.version; stored push/pull results carry the
  version they describe (push_version from the server's echoed version,
  or the current version at receipt for servers that don't echo one;
  pull_version captured at request send so an in-flight pull that a
  didChange races past is stale on arrival).
- fresh == tag >= version. Invalidation is implicit in the bump — no
  store-clearing, no clock comparisons, no race windows.
- _has_fresh_push/_has_fresh_pull helpers dissolve into two one-line
  _DocState methods; diagnostics_for(fresh_only=True) becomes a
  three-liner.

Semantics are unchanged from f9b1fd799 (same tests pass, one test
updated off private internals); net -15 lines.
2026-07-20 13:43:36 -04:00
ethernet
a632e68a01 fix(lsp): never report stale diagnostics — wait for fresh post-edit data
Slow language servers (tsserver on large projects especially) publish
diagnostics long after an edit. The client's wait/report path had three
holes that together surfaced the PREVIOUS edit's errors as if they were
current ("ghost diagnostics"), sending the agent chasing errors it had
already fixed:

1. open_file only cleared the diagnostic stores on first open — on the
   didChange path (every subsequent edit) stale push/pull entries
   survived.
2. wait_for_diagnostics' predicates were satisfiable by that leftover
   state (`path in _published`, `path in _pull_diagnostics`), so the
   "wait" often returned instantly with old data.
3. diagnostics_for merged the stale push store unconditionally, so even
   a fresh clean pull got the old error merged back in.

Fix: anchor freshness on a per-file didChange timestamp.

- Pull results record their request send-time and are dropped when a
  didChange raced past them; the pull store is invalidated on every
  change, not just first open.
- wait_for_diagnostics now returns bool (fresh data vs timeout), only
  counts pushes published at/after the change (and version >= ours when
  the server echoes versions), and accepts an explicit timeout — the
  user's lsp.wait_timeout config now actually controls the inner wait
  budget instead of only the outer thread-join.
- diagnostics_for(fresh_only=True) excludes stores that predate the
  latest change; all manager report paths use it.
- On timeout the manager returns [] ("no data") instead of stale
  state, logs a WARNING via eventlog, and does NOT mark the server
  broken — slow is not dead.
- seed-on-first-push no longer marks the file published, so the TS
  seed push can't satisfy a waiter.

Tests: new "stale" and "slow_push" mock-server scripts model the slow
tsserver, plus client- and service-level regression tests
(tests/agent/lsp/test_stale_diagnostics.py).
2026-07-20 13:43:36 -04:00
Brooklyn Nicholson
9b513a3b8d refactor(desktop): hoist shared ToggleRow into settings primitives
System + Notifications each had an identical local ToggleRow; lift one
haptic-baked version into primitives and reuse it. Net -12 lines.
2026-07-20 12:31:06 -05:00
ethernet
6ddbe8e5a4 revert(installer): revert managed uv changes for now
windows e2e ain't ready yet
2026-07-20 13:19:37 -04:00
Teknium
456f18b19c fix(picker): scope exact-ID resolution to lossy alias collapses only
The cherry-picked resolve_provider_full 0.5 step returned a generic
openai_chat ProviderDef for ANY registry ID, hijacking single-entry
alias rewrites like copilot -> github-copilot away from their overlay
transports (test_explicit_copilot_switch_uses_selected_model_api_mode
regression). Restrict the early return to names where MULTIPLE registry
providers collapse to one canonical (kimi-coding + kimi-coding-cn +
kimi + moonshot -> kimi-for-coding) — the only case where alias
resolution actually loses information.

Also maps Almurat123's contributor email.
2026-07-20 10:17:57 -07:00