fix(zai): add fetch_models override for GLM-5.2 + base_url fix - #46615
Closed
ether-btc wants to merge 71 commits into
Closed
fix(zai): add fetch_models override for GLM-5.2 + base_url fix#46615ether-btc wants to merge 71 commits into
ether-btc wants to merge 71 commits into
Conversation
…stubs The prune pass replaces old tool results with 1-line stubs. Without error indicators, a FAILED terminal command looked identical to a successful one — downstream context lost the signal that something went wrong. Changes: - Unified JSON parsing: extract exit_code and stderr from a single json.loads() call, falling back to regex for non-JSON output - Nonzero exit codes now produce a 'FAILED' tag in the stub - If stderr is present, includes a 120-char preview in the FAILED tag - Signal-killed processes (negative exit codes) are handled correctly - 6 new tests in TestSummarizeToolResultErrorIndicators Audit: 3-cycle review (self-review + subagent review + final validation) 108/108 tests pass (97 compressor + 11 plugin)
GLM-5.2 is the latest flagship on the Z.AI GLM Coding Plan (https://docs.z.ai/devpack/latest-model). 1M context, 131K max output, reasoning-capable, text-only input. Changes: * hermes_cli/models.py — add glm-5.2 to _PROVIDER_MODELS['zai'] (curated static catalog, leads the list) * plugins/model-providers/zai/__init__.py — set explicit models_url=https://api.z.ai/api/paas/v4/models (defense-in-depth per model-provider-picker-integration skill layer 1), prepend glm-5.2 to fallback_models, document the GLM Coding Plan endpoint alternative * tests/hermes_cli/test_zai_picker.py (new, 15 cases) — covers profile fields, static catalog, offline no-key fallback, live fetch path, and validate_requested_model Note: z.ai's /v1/models endpoint does not currently list glm-5.2 in its public catalog for most users, so the live picker won't surface it. The static catalog + validator accept glm-5.2, and `hermes chat -m glm-5.2 --provider zai` works end-to-end against the Coding Plan endpoint. The 38-case zai/glm test sweep (new + existing) passes.
…Index The desktop code uses Array.prototype.findLast (chat/composer/index.tsx) and findLastIndex (session/hooks/use-session-actions.ts), which are ES2023 APIs, but tsconfig declared only the ES2022 lib. Some TypeScript builds tolerate this, but a correct/stricter tsc fails the desktop build with: TS2550: Property 'findLast' does not exist on type 'ChatMessage[]'. Do you need to change your target library? Try changing 'lib' to 'es2023'. Declare es2023 so the build is correct regardless of the resolved TypeScript version (reported on Windows with Node 24). Refs NousResearch#38970
…ousResearch#42614) hermes update pulls the latest repo, so the freshly-pulled website/static/api/model-catalog.json is already the newest catalog. Copy it straight over ~/.hermes/cache/model_catalog.json instead of relying on a network fetch (which can be Vercel bot-gated or hit a Portal hiccup and silently degrade the picker to a stale/short list). Adds seed_cache_from_checkout() in model_catalog.py (read shipped manifest, validate, atomic write via _write_disk_cache, reset in-process cache) and calls it from both update paths in main.py: _cmd_update_impl (git pull) and _update_via_zip (Docker/no-git). Non-fatal on missing/malformed/invalid files — the normal network refresh still applies on next picker open.
…ocess calls When Hermes runs in TUI mode, the gateway child process communicates with the Node.js parent over a JSON-RPC protocol on stdin. Subprocess calls that inherit this stdin fd can trigger a race condition where the child's stdin read returns EOF, causing the gateway to exit cleanly (exit code 0) mid-tool- execution. This is the same root cause as issue NousResearch#14036 (byterover plugin) and PR NousResearch#39257 (SSH environment backend). This commit applies the fix — stdin=subprocess.DEVNULL — to all 85 subprocess.run() and subprocess.Popen() calls that execute inside the TUI gateway child process. Scope: TUI-context code only (agent/, tools/, plugins/, tui_gateway/server.py). CLI code (cli.py, hermes_cli/), tests, scripts, and gateway process management are excluded — they don't run inside the TUI child and inherit the terminal's stdin, not the JSON-RPC pipe. 85 call sites across 28 files. All files pass syntax check.
scripts/check_subprocess_stdin.py scans agent/, tools/, plugins/, and tui_gateway/ for subprocess.run() and subprocess.Popen() calls that don't explicitly set stdin=. Missing stdin= means the child inherits the parent's fd, which in TUI mode is the JSON-RPC pipe — causing gateway crashes on stdin EOF. Exits 0 (pass) or 1 (violations found). Can be run manually or added to CI. Skips comments, docstring references, and calls that use input= (which creates its own pipe). Usage: python scripts/check_subprocess_stdin.py
Wraps scripts/check_subprocess_stdin.py as a pytest so CI catches regressions when new subprocess calls are added without stdin=.
The blanket DEVNULL pass muzzled run_oauth_setup_token()'s interactive 'claude setup-token' login, which needs inherited stdin to prompt the user. Revert that one call and replace the guard's brittle file:line whitelist with an inline 'noqa: subprocess-stdin' marker that travels with the code.
Regression tests for the salvage follow-up: the interactive 'claude setup-token' login must keep inherited stdin, and the guard's inline 'noqa: subprocess-stdin' marker must exempt a call.
The blanket stdin=subprocess.DEVNULL pass added the kwarg to the docker 'version' preflight call; the test pinned the exact kwargs dict. Update the expected dict to match.
…sResearch#42616) image_generate returns its artifact as JSON ({"image": "/abs/path.png"}) with no MEDIA: tag, so the gateway auto-append path (which only recognized text_to_speech MEDIA: tags) never delivered it — image delivery silently depended on the model restating the path in its reply. Add image_generate to the producer allowlist and extract the local path from its JSON result (host_image > image > agent_visible_image), reusing the existing extension-anchored matcher and history-dedupe so remote URLs, unknown extensions, failures, and already-sent paths are rejected. Closes the remaining unfixed path from NousResearch#19105.
Store operator and assigned iMessage numbers in `auth.json` after setup, and surface them in `hermes photon status`. When numbers are missing, status auto-refreshes from the dashboard without provisioning new lines.
Switch `list_users`, `find_user_by_phone`, `create_user`, `register_user_if_absent`, and `refresh_user_numbers` from the Dashboard API (Bearer token) to the Spectrum API (Basic auth with project credentials). Update response unwrapping to handle the nested `data.users` envelope returned by Spectrum, add `_spectrum_host()` resolver, `_basic()` header helper, and structured error helpers. Update tests, docs, and plugin.yaml accordingly.
Extend the sidecar and Python adapter to handle `voice` content alongside `attachment`. Voice notes are inlined as base64 (same size-cap logic), surfaced as `MessageType.VOICE`, and include an optional `duration` field in fallback markers when bytes are unavailable.
Follow-up to the salvaged fallback-chain fix: - Replace the hand-rolled fallback loader with the shared hermes_cli.fallback_config.get_fallback_chain() helper so the TUI path matches HermesCLI and gateway/run.py exactly: fallback_providers stays first and keeps order, with distinct legacy fallback_model entries merged in after (deduped). Previously the TUI loader picked one key OR the other, diverging from CLI/gateway when both were set. - Update the test to assert the merged canonical semantics. - Add psionic73 to scripts/release.py AUTHOR_MAP (CI gate).
…rt race (NousResearch#42626) The autouse _suppress_concurrent_hermes_gate fixture did monkeypatch.setattr(main, '_detect_concurrent_hermes_instances', ...) with no raising=False. Its try/except guards the import but not the setattr, so under pytest's per-test spawn isolation a transiently partial hermes_cli.main module (one a concurrent worker is mid-importing) made setattr raise AttributeError and errored unrelated tests in the slice. Add raising=False so a transiently-absent attribute is a no-op default rather than a hard error. The attribute always exists once main.py finishes importing; the real-function opt-out (@pytest.mark.real_concurrent_gate) is unaffected.
… list (NousResearch#42629) Two new free-tier slugs surfaced in /model and `hermes model`. owl-alpha was already present. Regenerated website/static/api/model-catalog.json to keep the manifest sync test green.
…ortal failure (NousResearch#42628) The Portal's /api/nous/recommended-models endpoint is the source of truth for which models are free/paid right now, but its result was cached in-process only. When the live fetch failed (network, parse, non-2xx), the function returned {} and the model picker silently dropped the free/paid recommendations — free models would vanish with no indication anything went wrong. Add a per-base disk cache at $HERMES_HOME/cache/nous_recommended_cache.json: a successful live fetch is persisted as last-known-good, and a failed fetch with an empty in-process cache falls back to the disk copy instead of {}. Self-heals on the next successful fetch. With no disk copy, still degrades to {} (callers already handle that). Keyed by portal base URL so staging/prod don't collide. E2E: live fetch writes disk; simulated Portal failure returns the cached free models from disk; no-disk + failure returns {}.
@file: attachments now work when the desktop is connected to a remote gateway. Previously a referenced file resolved to a client-disk path the gateway couldn't see, so context_references rejected it with "path is outside the allowed workspace" and the agent never saw the file. Adds a file.attach RPC (sibling to the existing image.attach_bytes / pdf.attach byte-upload pipeline): the desktop uploads the file bytes, the gateway stages them into <workspace>/.hermes/desktop-attachments/ and returns a workspace-relative @file: ref that resolves cleanly. Local mode passes the path directly; a gateway-visible file outside the workspace is copied in; an in-workspace file is referenced as-is with no copy. Consolidates the file-sync design from NousResearch#38615 (LeonSGP43) and the host-file-staging idea from NousResearch#33455 (Carry00), rebased onto the image/PDF remote-media helpers already on main. Co-authored-by: LeonSGP43 <cine.dreamer.one@gmail.com>
…NousResearch#42695) `test_terminal_config_env_sync.py::_save_config_env_sync_keys()` AST-scanned `hermes_cli/config.py:set_config_value` for a `_config_to_env_sync = {...}` literal. The terminal-config env bridging was consolidated onto the canonical `TERMINAL_CONFIG_ENV_MAP` (now read via `terminal_config_env_var_for_key()`), so that literal no longer exists and the scanner raised: AssertionError: Could not find `_config_to_env_sync = {...}` literal in source failing 8 of 9 tests on main for every PR. Read the live `TERMINAL_CONFIG_ENV_MAP` instead — the actual source of truth `set_config_value` bridges through — mirroring its `terminal.cwd` exclusion. Refresh the stale module docstring and the now-incorrect error-message hints that still referenced `_config_to_env_sync`. Verified: the suite goes green, and a mutation (dropping `docker_volumes` from `TERMINAL_CONFIG_ENV_MAP`) still trips the pinned regression test, so the drift guard retains its teeth.
…search#35614) (NousResearch#42653) * fix(terminal): complete sane PATH entries on POSIX Fixes macOS gateway/launchd terminal sessions whose PATH already includes /usr/bin while omitting Apple Silicon Homebrew paths. LocalEnvironment._make_run_env() now appends each missing _SANE_PATH entry individually on POSIX, preserving caller precedence and avoiding duplicate sane entries. Root cause: the previous logic used /usr/bin as the sentinel for sane PATH injection. macOS launchd commonly provides /usr/bin while leaving out /opt/homebrew/bin and /opt/homebrew/sbin, so Homebrew-installed CLIs stayed unavailable in terminal tool calls. Salvaged from NousResearch#35614 by @y0shua1ee. Fixes NousResearch#35613. Co-authored-by: y0shua1ee <104712437+y0shua1ee@users.noreply.github.com> * test(terminal): harden sane PATH completion against dup/empty entries Follow-up to the NousResearch#35613 fix. Strengthens _append_missing_sane_path_entries: - De-duplicate the caller-supplied PATH (first occurrence wins) so a PATH that already contains duplicate entries is collapsed rather than carried through. Previously only newly-appended sane entries were guarded against duplication; pre-existing caller duplicates were preserved verbatim. - Drop empty PATH entries (leading/trailing/double ':'), which POSIX shells interpret as the current working directory — a mild foot-gun in a default terminal environment. Behaviour for well-formed PATHs (no duplicates, no empty entries) is byte-identical to before; only malformed/duplicated inputs change. Adds regression tests for: the literal macOS launchd PATH (/usr/bin:/bin:/usr/sbin:/sbin), caller-duplicate collapsing with order preservation, and empty-entry stripping. * docs(terminal): clarify PATH normalisation semantics; drop dead set add Addresses review findings on the sane-PATH completion follow-up: - Sharpen the _append_missing_sane_path_entries docstring to state explicitly that on POSIX the caller PATH is rewritten (empty entries stripped, duplicates collapsed) rather than merely appended to, and that well-formed PATHs remain byte-identical bar the appended sane entries. This makes the intentional semantic change visible rather than buried under "hardening". - Document why _path_env_key is a deliberate second Windows guard distinct from the helper's early return (key-casing selection vs standalone safety), so neither is mistaken for redundant and removed. - Drop the dead `seen.add(entry)` in the sane-entry loop: _SANE_PATH is a static duplicate-free constant, so the membership check against the caller entries is sufficient and `seen` is never read afterwards. No behaviour change: verified byte-identical output across the launchd, minimal, empty, duplicate, empty-entry and already-full cases, and re-confirmed gh/brew resolve through the real LocalEnvironment.execute() path under a launchd-style PATH. 133 targeted tests pass. Intentionally NOT consolidating with tools/browser_tool._merge_browser_path: it prepends (vs append), filters on os.path.isdir, uses os.pathsep, and draws from a dynamic candidate set — a shared helper is a separate refactor, out of scope for this bugfix. --------- Co-authored-by: y0shua1ee <104712437+y0shua1ee@users.noreply.github.com>
…usResearch#42729) The markdown code-block change rendered args['command'] in full in both verbose AND non-verbose (all/new) modes, so a long or multi-line terminal command bypassed the tool_preview_length cap (default 40) and rendered as a huge block. Non-verbose now collapses to a single line capped at the preview length while keeping the fence; verbose keeps the full command.
…usResearch#42691) Based on NousResearch#42658 by @mnajafian-nv. Preserves the real downstream provider/tool exception when NeMo Relay's managed adaptive execution wraps a failing callback as an internal runtime error. Without this, the original exception (and its retry-classification signal, e.g. status_code) is lost behind Relay's wrapper. Salvage changes on top of the original PR: - Tolerant Relay-wrapper match: _is_relay_wrapped_callback_error now uses str.startswith on the "internal error: <cls>: <msg>" prefix instead of exact equality, so a future Relay version appending a traceback/suffix doesn't silently defeat the unwrap. On a total format change it returns False and falls back to the pre-fix behavior (surfacing Relay's error) rather than masking it. - Deduplicated the LLM and tool execute paths into a shared _run_managed_with_downstream_preservation helper, removing ~20 lines of copy-pasted nonlocal/try-except scaffolding that could drift out of sync. - Added a real-middleware regression guard (test_nemo_relay_downstream_unwrap_matches_real_middleware_wrapper_shape) that drives hermes_cli.middleware._run_execution_chain and asserts the plugin's _original_downstream_error unwraps the actual private _DownstreamExecutionError wrapper. The original synthetic tests modeled the wrapper with a local class, so a rename or shape change in core middleware would not have been caught; this test fails loudly if that contract drifts. Co-authored-by: mnajafian-nv <mnajafian@nvidia.com>
…Research#42856) * docs(windows): correct native data dir to %LOCALAPPDATA%\hermes The Windows-native guide claimed a deliberate split where config, auth, skills, and sessions live under %USERPROFILE%\.hermes. That is not what the installer does: scripts/install.ps1 sets HERMES_HOME=%LOCALAPPDATA%\hermes, so data actually lives in %LOCALAPPDATA%\hermes alongside the disposable install (the hermes-agent\, git\, node\, bin\ subdirectories) — `hermes config` confirms config.yaml/.env resolve there, not under %USERPROFILE%. Update the data-layout table, the "split is deliberate" note, the env-var and uninstall sections to describe the real layout: data and install share the %LOCALAPPDATA%\hermes root, reinstall only replaces hermes-agent\, and a full wipe targets %LOCALAPPDATA%\hermes (with %USERPROFILE%\.hermes kept only as a legacy/WSL cleanup). Mention HERMES_HOME as the override knob. * docs(windows): fix PATH + bin layout to match installer The installer adds hermes-agent\venv\Scripts (where hermes.exe lives) to User PATH and sets HERMES_HOME — not %LOCALAPPDATA%\hermes\bin. The \bin dir holds Hermes's managed uv.exe, not a hermes.cmd shim. Correct the install-step list and the data-layout table accordingly. * fix(install): show real HERMES_HOME path in setup messages The native Windows installer wrote config/env/skills under $HermesHome (%LOCALAPPDATA%\hermes) but its success messages claimed ~/.hermes, which doesn't exist on native Windows. Print the actual paths so a new user can find their config, .env, and skills.
its so slow
…earch#43103) The Anthropic picker returned the live /v1/models dump verbatim whenever credentials were configured. Anthropic's API lags newly-routed curated aliases (e.g. claude-fable-5, reachable on Anthropic before the models endpoint enumerates it), so the curated entry vanished from the picker. Merge curated _PROVIDER_MODELS["anthropic"] with the live catalog — curated first, live-only appended, deduped — mirroring the OpenAI curated-merge path. Live failure / no creds falls back to curated verbatim.
Both jobs in tests.yml (`test` matrix and `e2e`) start from a cold uv cache on every run and install deps with `uv pip install -e ".[all,dev]"`, which re-resolves pyproject.toml ranges and rebuilds the editable install each time. Two changes: 1. Enable uv's official CI caching via setup-uv's `enable-cache: true`, keyed on pyproject.toml + uv.lock, plus `uv cache prune --ci` to keep the persisted cache small. Warm runs install from cache instead of re-downloading/building wheels. 2. Replace the manual `uv venv` + `uv pip install -e` with `uv sync --locked --python 3.11 --extra all --extra dev`. sync installs the exact pinned set from uv.lock (and fails if the lock is stale vs pyproject.toml), creating .venv itself. This is reproducible and, with a warm cache, measurably faster than the editable pip install (~3-4x on the steady-state install step locally). Downstream steps keep using `source .venv/bin/activate`; sync writes .venv to the same path. Follows the Astral-recommended pattern for uv in GitHub Actions: https://docs.astral.sh/uv/guides/integration/github/ Co-authored-by: Wesley Simplicio <wesleysimplicio@live.com>
…s reappear (NousResearch#43149) * fix(state.db): recover from malformed sqlite_master so hidden sessions reappear The corruption class behind "Desktop/Dashboard show no sessions while hundreds of session files sit on disk" is a malformed sqlite_master — most often a duplicate object row, e.g. two CREATE VIRTUAL TABLE messages_fts entries — surfacing as: sqlite3.DatabaseError: malformed database schema (messages_fts) - table messages_fts already exists SQLite parses the whole schema while preparing the FIRST statement on a connection, so on this class every statement fails before it runs: PRAGMA journal_mode (which is where SessionDB.__init__ actually trips, in apply_wal_with_fallback, BEFORE _init_schema), PRAGMA integrity_check, and even DROP TABLE. The only operations that still work are PRAGMA writable_schema=ON plus direct sqlite_master surgery. A plain FTS-index rebuild at the _init_schema layer therefore cannot reach or fix this; the canonical sessions/messages rows are intact — only the derived schema is broken. Add a dedicated recovery that operates where the failure actually happens: - hermes_state.repair_state_db_schema(): backs up the raw file first, then a least-destructive ladder — (1) de-duplicate sqlite_master keeping the lowest rowid per object (preserves the existing FTS index), escalating to (2) drop every messages_fts* schema object + VACUUM and let the next open rebuild the FTS index from messages. sessions/messages are never modified. Plus is_malformed_db_error() to discriminate this class. - SessionDB.__init__ auto-heals: on a malformed-schema open error it repairs once (process-guarded against loops / concurrent web_server opens) and reopens, so Desktop/Dashboard recover on their own instead of silently showing "no sessions". - hermes doctor --fix detects the malformed class and repairs it (reporting the recovered session count + backup name). - hermes sessions repair [--check-only] [--no-backup] runs on the raw file path, since SessionDB() itself cannot open a malformed DB. Supersedes NousResearch#32589 and NousResearch#33869: both targeted FTS corruption but gated their repair behind statements (integrity_check / SELECT / DROP TABLE) that themselves fail on this class, and neither addressed the apply_wal_with_fallback open-time failure. Credit preserved via Co-authored-by. Closes NousResearch#33865. Co-authored-by: João Vitor Cunha <145560011+plcunha@users.noreply.github.com> Co-authored-by: Tuna Dev <273476039+tuancookiez-hub@users.noreply.github.com> * test(state.db): cover strat-B escalation + unrepairable safe-fail paths --------- Co-authored-by: João Vitor Cunha <145560011+plcunha@users.noreply.github.com> Co-authored-by: Tuna Dev <273476039+tuancookiez-hub@users.noreply.github.com>
Finder/OS drops became `@file:/Users/...` refs that only resolve when the gateway shares the local disk, so on a remote gateway non-image files (PDF/CSV/Markdown/...) never reached the agent. Route OS drops through the file.attach / image.attach_bytes upload pipeline — in-app project-tree and gutter drags stay inline workspace-relative refs — across every drop surface: the conversation area, the composer form, the contenteditable input, and the message-edit composer (which still reproduced the bug). Also: - upload dropped files eagerly when a session exists, so the card shows a spinner instead of stalling the send (images stay submit-time to avoid racing their thumbnail write); - round the attachment card and drop the monospace detail; - render image previews from the bytes we already hold, so a pasted/dropped screenshot shows its thumbnail and previews even when its only on-disk copy is a transient path (the data URL is not persisted to localStorage). Supersedes NousResearch#38615, NousResearch#41203. Co-authored-by: LeonSGP <154585401+LeonSGP43@users.noreply.github.com> Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
The in-flight user bubble seeded image attachment refs as `@image:<localpath>`.
In remote-gateway mode that path lives on the desktop, not the gateway, so the
inline thumbnail fetch hit /api/media and 403'd ("Path outside media roots"),
flashing a fallback chip until submit uploaded the bytes.
Seed (and keep) image refs as the raw base64 preview data URL instead. It
renders inline via extractEmbeddedImages with zero network, and survives the
post-sync rewrite (the agent gets the bytes through the attached-image pipeline,
not this display ref) so the thumbnail no longer remounts/flashes. Non-image
refs are unchanged.
Adds optimisticAttachmentRef + unit coverage.
Two races in the drop-time eager upload: - Resurrected chip: the success path used addComposerAttachment, which re-appends when the id is gone, so a file removed mid-upload reappeared once the upload resolved. Add updateComposerAttachment (update-only; no-op when the chip was removed) and use it on both the eager success path and submit-time sync. - Duplicate upload: submit-time sync didn't join an eager upload still in flight, so drop-then-Enter could fire file.attach twice and leave a duplicate under .hermes/desktop-attachments/. Track in-flight eager uploads by id and await the pending one before deciding to re-upload, reusing its gateway ref. Tests: composer-store no-resurrect unit tests + a join-on-submit integration test asserting a single file.attach. Addresses @helix4u review on NousResearch#43109.
…ops upload The message-edit composer staged dropped OS files asynchronously with no visible state, so confirming the edit before the upload resolved could send the message without the gateway-side ref (helix4u review note on NousResearch#43109). Add a staging flag: while uploadOsDropRefs is in flight, show a small spinner pill in the bubble and block submit (disabled send button + submitEdit guard) so the edit can't outrace the ref insertion. New `attachingFile` i18n string across en/zh/zh-hant/ja.
…6MB cap Remote attachments read their bytes through the readFileDataUrl IPC, which is hard-capped at 16MB and rejects with a raw "file is too large (N bytes; limit M bytes)" string straight into the failure toast (helix4u review note on NousResearch#43109). Translate that into "<file> is too large to upload to the remote gateway (max 16 MB)", parsing the limit out of the message so it tracks the real cap. Applies to both the image and non-image remote read paths; non-cap errors pass through unchanged. Adds unit coverage for both.
…ad end A binary @file: ref (PDF, docx, spreadsheet, …) expanded to a bare "binary files are not supported" warning with no content. The model saw a failure and gave up — e.g. a dropped PDF came back as a text note claiming the type was unsupported, even though the file was staged on disk right next to it. Inject an actionable content block instead: the path, mime type, size, and a nudge to use its tools to read/convert/view the file (and explicitly not to tell the user the type is unsupported). General across every binary type — not PDF-specific. The file already resolves where the agent's tools run (local cwd or the staged copy in a remote session workspace), so it can act on it directly.
…42871) The "..." overflow that opens the profile manager (the only UI to edit a profile's SOUL.md) was gated behind profiles.length > 1, so a user with only the default profile couldn't edit its persona without first creating a throwaway second profile. Render it unconditionally.
…ile switch (NousResearch#40892) * fix(tui_gateway): honor target profile's terminal.cwd on desktop profile switch The desktop's app-global remote mode serves every profile from one tui_gateway backend, so the process-global TERMINAL_CWD only reflects the launch profile. After switching profiles, a new session resolved its workspace from that stale env var and inherited the previous profile's directory. Add _profile_configured_cwd() to read a non-launch profile's own terminal.cwd from its config.yaml (skipping placeholder/empty/missing and non-existent paths so callers fall back cleanly), and wire it into _completion_cwd() with precedence: explicit client cwd -> existing session cwd -> bound profile's configured cwd -> TERMINAL_CWD -> os.getcwd(). Fixes NousResearch#40334 * test(tui_gateway): cover per-profile cwd resolution (NousResearch#40334) Pin the new contract: _profile_configured_cwd reads a profile's own terminal.cwd and rejects placeholders/missing paths, and _completion_cwd prefers a bound profile's cwd over a stale launch-profile TERMINAL_CWD while still letting an explicit client cwd win.
… state (NousResearch#39639) * fix(desktop): send on Enter from live editor text, not stale composer state Pressing Enter often did nothing (~90% with IME / fast typing); adding a trailing space "fixed" it. The composer's submit path read the draft from the AUI composer state (`useAuiState(s => s.composer.text)`) and the derived `hasComposerPayload`, both of which lag the contentEditable DOM by a render. On fast typing or IME composition the final keystroke(s) weren't in state yet, so `submitDraft()` saw an empty draft and dropped the message. A trailing space only worked around it by forcing an extra input event that flushed the state. submitDraft() now refreshes draftRef from the editor node and submits/queues based on the live DOM text, and the Enter handler decides the queue-drain vs submit branch from the DOM too. draftRef is already synced on every input event, so this just closes the in-flight-keystroke gap. Fixes NousResearch#39630. Also addresses the "typing + Enter does nothing" reports in NousResearch#39623. * test(desktop): cover Enter-submit from live editor text (NousResearch#39630) Pin the contract that the composer's Enter path reads the live DOM editor text, not the render-lagged composer state: a just-typed message sends even when state hasn't synced; while busy it queues (never drains the queue or cancels); an empty Enter while busy is a no-op; and an empty idle Enter drains the next queued prompt. Faithful DOM-event repro mirroring handleEditorKeyDown + submitDraft.
glm-5.2 is callable on z.ai but not listed by /v1/models. The fallback_models list already includes it, but when the live catalog returns successfully, glm-5.2 was missing from the merged list. _ZaiProvider.fetch_models() merges live + extra models.
GLM-5.2 and several GLM-4.5 variants work via /chat/completions but are absent from the /models catalog endpoint. Without a fetch_models override, the model picker silently drops these models. Changes: - Add ZAIProfile(ProviderProfile) with fetch_models() that merges the live catalog with a curated _EXTRA_MODELS set, falling back to the full set if the catalog is unreachable - Fix base_url from /api/paas/v4 to /api/coding/paas/v4 (the endpoint that actually serves GLM-5.2) - Expand fallback_models with glm-4.5, glm-4.5-air, glm-4.5-flash
Contributor
|
This is broken PR that pulls in a lot. Please reopen from latest |
Contributor
Author
|
You're right — the diff is bloated (8.7k additions across 151 files) because the branch was forked from a stale snapshot and carried forward ~146 unrelated file changes. Plan to reopen from latest:
The fix solves: GLM-5.2 (and several GLM-4.5 variants) are functional via Will reopen (or open fresh PR) once the squashed version is ready. Estimated diff: ~400-600 lines across 5 files, +200 tests. |
Contributor
Author
|
Reopened as clean PR: #46629 Single file changed ( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
GLM-5.2 and several GLM-4.5 variants work via
/chat/completionsbut are absent from the/modelscatalog endpoint. Without afetch_modelsoverride, the model picker silently drops these models — the user can't select them even though they're functional.Additionally, the
base_urlwas pointing to/api/paas/v4instead of/api/coding/paas/v4, which is the endpoint that actually serves GLM-5.2.Changes
Add
ZAIProfile(ProviderProfile)with afetch_models()override that:super().fetch_models()_EXTRA_MODELSset (8 known-working models)_EXTRA_MODELSlist if the catalog is unreachableFix
base_url:/api/paas/v4→/api/coding/paas/v4Expand
fallback_models: addedglm-4.5,glm-4.5-air,glm-4.5-flashVerification
Tested on a Raspberry Pi 5 running Hermes Agent v0.16.0. GLM-5.2 is the daily driver model — it works reliably via
/chat/completionsbut was invisible in the model picker until this fix was applied.