feat(skills): add 7-agent-pipeline — mandatory multi-agent workflow - #6
Open
bbudiono wants to merge 876 commits into
Open
feat(skills): add 7-agent-pipeline — mandatory multi-agent workflow#6bbudiono wants to merge 876 commits into
bbudiono wants to merge 876 commits into
Conversation
…eview-open feat(desktop): let the agent drive the shell — preview pane + pane focus
…d age util - Use the fork glyph for branch and a sine wave for read aloud (all one lib now) - Extract compact "2h ago" into formatAgo() in lib/time.ts (+ ageDays locale string) - Cover formatAgo with a unit test
A stray tsc run can emit foo.js next to foo.ts under apps/shared/src or apps/desktop/src. .gitignore hides the artifact from git status, but Vite resolves extensionless imports .js-before-.ts, so the renderer silently runs the stale compiled copy. tsc -b . --clean already knows the emit graph and deletes matching outputs. Run it before vite in all dev scripts. This bit for real: a Jul 16 artifact of websocket-url.js predated the NousResearch#68250 getGatewayWsUrl contract change ({ ok, wsUrl } IPC result), so its old 'if (fresh) return fresh' handed the whole result object to new WebSocket(), dialing ws://127.0.0.1:5174/[object%20Object] on every boot. The desktop app could never connect, and the failure survived reboots and cache wipes because the poison lived in src/. JsonRpcGatewayClient.connect() now rejects non-ws:// URLs with a readable error instead of letting new WebSocket() coerce an object into [object%20Object], so any future contract skew fails diagnosably.
…-actions Flatten assistant message actions into an inline icon row
…ousResearch#54242) A pure-Latin query (no CJK characters) routes to the unicode61 `messages_fts` table, whose tokenizer does not insert a boundary between Latin letters and adjacent CJK characters. Content like "修改youer服务端" is indexed as a single token, so `search_messages("youer")` returned zero results even though the substring is present, and the Latin path had no fallback. Add a zero-result trigram fallback to the pure-Latin path: when the unicode61 search misses, retry against the existing `messages_fts_trigram` table, which matches substrings regardless of word boundaries. The fallback is gated on `_trigram_available` and on every token being >=3 chars (the trigram minimum), and only fires on a zero-result miss, so successful Latin searches keep their unicode61 ranking unchanged. The trigram query construction shared with the CJK path is extracted into a `_run_trigram_search()` helper; the CJK branch is refactored to use it with no behavior change. Adds regression tests in tests/test_hermes_state.py::TestCJKSearchFallback.
The zero-result fallback prefers messages_fts_cjk when built: exact ranked token match for Latin runs unicode61 fused onto CJK, including <3-char tokens the trigram leg can't recover.
…aces Two narrow timing windows (reported by null-runner) silently downgraded a mid-turn correction to a plain next-turn message on the desktop client: - Turn-build window: a fresh turn flips running=True and builds the agent asynchronously, so session["agent"] is briefly None. session.redirect answered 4010 "unsupported", which the renderer's catch swallowed into a lost follow-up. Queue the correction server-side instead and return status="queued" — lossless, and honest about what happened. - Stale runtime id after reconnect: session.redirect 404s on a sid the gateway no longer maps. redirectPrompt now resumes the stored session and retries once, mirroring stopPrompt, so a correction fired right after a reconnect isn't dropped. The desktop treats "queued" like "redirected": the correction reaches the model either way, so it's recorded once as a real user message.
…board), not just stdio The cross-surface theme SDK's live-repaint relies on a gateway skin watcher that polls config and emits skin.changed on any move. But that emit is session-less and fires from a background thread, so write_json fell through its (session-transport -> contextvar -> stdio) ladder to the module stdio transport — which only reaches the stdio TUI (tee'd to the dashboard WS publisher). WS clients (the desktop app, dashboard chat) never got it, so 'Hermes themes itself' repainted the CLI/TUI but not the GUI. Add a live-transport registry (one entry per connected WS peer, maintained by handle_ws) and a _broadcast_global_event primitive that fans session-less announcements out to every connected client, falling back to write_json when none are registered (stdio path unchanged). Route both skin.changed emits (watcher + the /skin RPC) through it, so a skin switch from any surface repaints all of them. Backend-only; desktop already handles skin.changed and does not drop session-less events.
…ldowns (NousResearch#69494) When Codex returns 429 usage_limit_reached, Hermes persists the provider's reset_at on the pool entry and freezes the credential until it elapses -- which can be days out for weekly windows. But the upstream window can reopen EARLY: the user redeems a banked rate-limit reset (Codex CLI / ChatGPT UI), upgrades their plan, or OpenAI resets the window. Hermes never re-checked, so it kept erroring with 'Codex provider quota exhausted (429); retry after Ns' until a manual re-auth rewrote the tokens (issue NousResearch#43747, externally-reset variant). - hermes_cli/auth.py: add _probe_codex_quota_restored() -- a throttled (5 min/token) GET of the Codex /usage endpoint; quota counts as restored when every reported window is <100% used. Add clear_codex_pool_quota_cooldowns() to lift 429/quota-shaped cooldowns from persisted pool entries (DEAD and auth-shaped entries untouched). - resolve_codex_runtime_credentials(): before surfacing a pool-only cooldown as 'quota exhausted', probe upstream; on a positive probe clear the cooldown and return the pool credential. - agent/credential_pool.py: _available_entries() probes frozen openai-codex entries (clear_expired path only) and unfreezes them when upstream confirms the reset. - agent/account_usage.py: a successful /usage reset redemption now clears persisted pool cooldowns immediately. Negative paths preserved: probe 429/exhausted/indeterminate keeps the cooldown; read-only enumeration never probes; non-JWT tokens never probe (no network in hermetic tests).
…ed the activation Real-world failure from dogfooding the live-theme flow: display.skin was already 'synthwave' in config, but the desktop never visibly applied it (the activation event predated the WS transport fix / the connect). The desktop's gateway.ready seed records the baseline WITHOUT painting (by design — never stomp the persisted desktop theme on connect), so it believed it was synced. Re-running 'hermes config set display.skin synthwave' then did nothing twice over: the watcher signature (name, skin-file mtime) hadn't moved, so no skin.changed fired; and even on an event, the desktop's name-equality guard blocked the apply against the seeded baseline. Two halves: - hermes_cli: setting display.skin touches the named skin file so the watcher signature always moves on an explicit set — a same-name re-affirm now broadcasts skin.changed like any real move. Built-ins (no file) are unaffected; a name switch already moves their signature. - desktop: track whether the synced baseline was actually APPLIED vs merely seeded at connect. A skin.changed matching a seed-only baseline is an intentional apply and repaints; once applied, repeat same-name events stay no-ops (protects a manual desktop-side theme switch from snap-back, incl. across a reconnect re-seed).
…hadow-guard fix(desktop): clean stale tsc emit + guard gateway WS URLs
…ousResearch#54855) (NousResearch#67364) * chore(gitignore): ignore installer .install_method stamp Salvage of NousResearch#54855 by @drissman — rebased onto current main with root-scoped rule and sister-marker comments alongside .update-incomplete. Closes NousResearch#66189 Root cause: scripts/install.sh writes <install>/.install_method but git did not ignore it, so managed checkouts show ?? .install_method and hermes update may autostash the untracked marker. Fix: add /.install_method to .gitignore (repo-root only). Verification: git check-ignore -v .install_method * test(update): assert .install_method survives update autostash (NousResearch#66189) Add hermetic regression mirroring the .hermes-bootstrap-complete test: adopt the real .gitignore, drop the installer .install_method stamp, run the exact 'git stash push --include-untracked' the updater uses, and assert the marker is neither swept nor reported dirty. Requested by hermes-sweeper review on NousResearch#67364.
…n-steering feat: redirect active turns when users correct the agent
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…ighten comments _emit and _broadcast_global_event were each building the JSON-RPC event envelope — extract _event_frame and use it from both. Type the registry as set[Transport] (protocol already imported), and cut comment bloat at the call sites. No behavior change; suites stay green.
…broadcast fix(themes): live skin sync reaches every surface — WS fan-out + missed-activation recovery
Live theme authoring's core loop — Hermes recolors the skin file it just activated — repainted the TUI but not the GUI. The event path was fine (post-NousResearch#69533 the WS broadcast lands and ingestBackendSkin refreshes the $backendThemes registry); the same-name apply guard also no-ops correctly (it's what protects a manual desktop theme pick). The repaint was supposed to come from the registry: the active theme IS that skin, its palette just changed. But ThemeProvider memoized deriveTheme on [themeName, resolvedMode] only, while deriveTheme reads the registry non-reactively via resolveTheme — so the store update re-rendered the provider and handed back the stale palette. Name switches repainted (themeName moves); recolors never did. Add the theme stores (user/backend/registry) to the memo's deps — they are deriveTheme's actual reactivity, same as the availableThemes memo directly above. applyTheme is idempotent, and $backendThemes only publishes on a real palette change, so no spurious repaints. Tests: render ThemeProvider for real — activation applies; a same-name recolor repaints (fails without the fix); an inactive-skin seed doesn't touch the painted theme.
…edit-repaint fix(themes): desktop repaints when the ACTIVE skin is edited in place
…ousResearch#69578) The submit "session context drift" guard (regression 7acaff5 / NousResearch#54527, partially fixed by 8c28876 and da52ffe) aborted a prompt submission whenever the selected stored id OR the route token changed mid-submit. Both signals churn programmatically on a busy gateway, so on machines with background streaming sessions, per-minute cron sessions, the Telegram surface, or gateway-profile switches, essentially every send from a second chat aborted silently: the optimistic message was dropped, the draft was left in the composer, no error was shown, and prompt.submit never fired. The false-positive churn sources were: - selection null-resets — gateway-switch's setSelectedStoredSessionId(null) on a gateway/profile switch or reconnect read as a switch away; - search/hash-only route-token changes — overlays and side panels park state in location.search/hash, so the pathname (the only part that selects a chat) was unchanged yet the raw token differed; - background-event active-ref retargets — createBackendSessionForSend's 3-prong check also watched activeSessionIdRef, which gateway events retarget while other sessions stream (NousResearch#47709 class), during a seconds-long session.create round-trip. New shared helper session-context-drift.ts reduces a route token to the chat it targets (pathname only; the new-chat route is '__new__', non-chat routes null) and reports drift only when selection or the routed chat moves to a DIFFERENT, non-null chat that is not the submit's own target. Selection null-resets, search/hash-only churn, and moves onto the submit target are no longer drift; genuine user switches (click another chat, click New Session mid-submit) still abort. Site A (submit.ts) routes all five guard points through the helper and logs '[submit-drift-abort]' with a per-site phase; the post-create active-ref check and baseline re-pin from 8c28876 are kept intact. Site B (createBackendSessionForSend) drops the active-ref prong entirely — every real switch retargets selection and route synchronously — and logs before closing the orphaned session. (cherry picked from commit b390e3a) Co-authored-by: Kennedy Umege <kenmege@yahoo.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…cated RPC routing (NousResearch#68229) * fix(desktop): route /compress through session.compress RPC with transcript replacement Salvages NousResearch#44462, NousResearch#53755, and NousResearch#68218 into a single canonical fix for the desktop /compress cluster. The desktop routed /compress through slash.exec, which sends it to the _SlashWorker subprocess. Compressing a large session outlives both the desktop's 30s WS timeout and the worker's 45s pipe timeout — the client gives up, runExec's blanket catch swallows the error, and command.dispatch surfaces a misleading "not a quick/plugin/skill command: compress" (NousResearch#44456). Even when compression succeeded via the _mirror_slash_side_effects path, the desktop never received the post-compress message list, so summarized bubbles stayed on screen forever — /compress looked like a no-op. This change routes /compress to the dedicated session.compress RPC (the TUI's path), combining the best of all three PRs: - 120s client timeout matching the TUI's HERMES_TUI_RPC_TIMEOUT_MS (NousResearch#44462) - Transcript replacement from the response `messages` via toChatMessages, the same converter session.resume uses (NousResearch#68218, teknium1 review on NousResearch#44462) - Session-isolation guard: updateSessionState only publishes for the active runtime, so a late result after a session switch can't clobber the foreground transcript (NousResearch#53755, teknium1 review on NousResearch#53755) - Coalescing: dedup concurrent compress requests per session (NousResearch#53755) - Progress toast ("compressing context...") outside the transcript (NousResearch#53755) - Error unmasking in runExec: when slash.exec fails and command.dispatch only adds "not a quick/plugin/skill command" routing noise, surface the original worker error instead (NousResearch#44462) - /compact alias + focus_topic forwarding Co-authored-by: AlliDev <AIalliAI@users.noreply.github.com> Co-authored-by: PinkEVO <PINKIIILQWQ@users.noreply.github.com> * feat(desktop): route slash commands with dedicated RPCs to those RPCs Salvages NousResearch#63513 — introduces a new `rpc` kind on DesktopCommandSurface so commands with a first-class gateway @method handler bypass slash.exec / command.dispatch entirely, and a `renderRpcResult` utility that shapes each RPC's structured reply into readable transcript text. Migrates 6 commands from exec() to rpc(...): /agents → agents.list /save → session.save /status → session.status /steer → session.steer /stop → process.stop /usage → session.usage /compress stays as action('compress') — it needs transcript replacement from the response `messages`, which the generic rpc path can't do (per teknium1 review on NousResearch#44462/NousResearch#63513). Also includes the json-rpc-gateway timeout message improvement: the error now includes the configured timeout duration ("request timed out after 120s: session.compress") so a user can tell whether the default 30s fired or a per-call override. Co-authored-by: Jelvin <SmallNew2003@users.noreply.github.com> * fix(desktop): preserve provider choice during config initialization * fix(desktop): preserve slash command and host compression semantics Keep commands whose CLI behavior exceeds their current RPC contracts on slash.exec. Propagate the full compression timeout through compute-host control, return structured host compression outcomes with metadata, and retain successful compression feedback in the desktop transcript. Add regressions for timeout forwarding, host aborts and metadata sync, structured host control responses, command routing parity, and numeric stop counts. * fix(desktop): harden compression state handling Preserve the invoking stored-session binding for delayed compression results, normalize replacement histories, and serialize provider selection. Stabilize gateway platform tests and guard the desktop Git facade during renderer teardown. --------- Co-authored-by: AlliDev <AIalliAI@users.noreply.github.com> Co-authored-by: PinkEVO <PINKIIILQWQ@users.noreply.github.com> Co-authored-by: Jelvin <SmallNew2003@users.noreply.github.com>
Exercise the full Electron, gateway, and mock-provider submit path while same-chat route query tokens churn during session creation. Assert the mock provider receives the prompt and its streamed response reaches the transcript.
* nous portal model pricing * update top message
Removes Homebrew and PyPI wheel/sdist as Hermes distribution paths while
preserving the supported source, Docker, and Nix workflows.
Changes:
- Removes the Homebrew formula, PyPI publish workflow, sdist manifest
(MANIFEST.in), and wheel/sdist release-attachment logic from scripts/release.py.
- Keeps setuptools metadata and entry points required by editable installs
and Docker/Nix builds, but adds a setup.py guard that rejects wheel/sdist
builds outside a sealed Nix derivation (HERMES_NIX_BUILD=1).
- Removes pip/Homebrew install detection, PyPI update checks, the pip
self-update path, the deprecation-banner state, the postinstall subcommand,
wheel data-directory fallbacks in agent/i18n.py and hermes_constants.py,
and the ACP Registry manifest/version-lockstep release logic.
- Adds /nix/store/ path detection so `nix run` / `nix profile install`
installs (which don't set HERMES_MANAGED) are correctly identified as
"nix" rather than falling through to "git"/"unknown".
- Retired install-method values ("pip", "homebrew") in existing
.install_method stamps (both code-scoped and home-scoped) are ignored by
the allowlist reader and fall through to "unknown" instead of resurrecting
a retired enum value.
- Updates Nix packaging to ship bare runtime data (locales, optional-mcps)
through store symlinks and wrapper env vars instead of wheel data-files.
- Removes the ACP Registry manifest/icon and their version-lockstep tests.
- Deletes or rewrites packaging, pip-update, Homebrew, and ACP Registry
tests; adds parametrized coverage for the packaging build guard covering
BOTH sdist and wheel paths (the guards live in separate cmdclass entries
— a passing sdist test proves nothing about the wheel path).
- Updates installation/platform documentation and related user-facing copy.
- Adjusts the supply-chain scan so deleted install-hook files do not trigger
a finding, while additions or modifications still require the existing
ci-reviewed label gate.
Supported installation paths (unchanged):
- git installer (install.sh)
- Docker
- Nix/NixOS
- editable development installs (uv sync, uv pip install -e ., pip install -e .)
…'s fg, not the skin's
Live-repaint's composer gap: flip a light terminal to a dark skin and the
input goes black-on-black. The placeholder was already explicit truecolor
(theme muted), but TYPED text rendered with no color at all — the terminal's
default foreground — in both paint paths:
- the Ink render (<Text wrap="wrap">{rendered}</Text>, no color), and
- the fast-echo bypass, which writes raw cells straight to stdout.
The skin owns the background (OSC-11) but the default fg still belongs to
the host terminal's polarity, so any skin/terminal polarity mismatch made
input invisible. Every other transcript line already paints
theme.color.text (the completed inputBuf rows directly above the composer).
Give TextInput a color prop and paint both paths with it: the Ink <Text>
(chalk re-opens the outer color after the placeholder chips' embedded [39m
closes; INV cursor/selection cells never touch fg) and the fast-echo write
via colorizeEcho — same explicit-truecolor-only rule as colorizeHint, so
the bypass cell can't flash terminal-default before the next frame. All six
TextInput sites (composer, prompts, masked, billing ×2, session switcher)
pass theme text; no color ⇒ passthrough, unthemed inputs keep the terminal
default.
Tests: colorizeEcho SGR wrap + passthrough contracts; full ui-tui suite
1338✓; typecheck clean.
…response-nudge fix(desktop): keep first response layout stable
…theme-color fix(ui-tui): input text goes invisible when a live skin flips the terminal's polarity
…ide the OSC-11 background The input fix's sibling, hit immediately after: the composer was themed but AGENT text went black-on-black the same way. Root cause is the class, not the call site — markdown body, borders, and every token rendered without an explicit color falls back to the terminal's DEFAULT foreground, which belongs to the HOST profile's polarity, not the skin's. A dark skin on a light terminal repaints the backdrop via OSC-11 while thousands of default-fg cells stay near-black. Chasing every <Text> is unwinnable. Instead own the default itself: when a skin authors a background (the existing opt-in), paint the default foreground from the resolved theme's text color via OSC-10. Every unthemed token — present and future — re-bases onto the skin atomically, exactly like the background. terminalModes: the OSC-11 slot generalizes to defaultColorSlot(10|11) — same paint/clear/exit-restore contract, tracked per slot, so a skinless session still never touches the terminal. reapplyTheme repaints the fg too: polarity flips swap paired palettes, moving the text tone while the background stays. Tests: slot contract runs table-driven over both OSC codes; handler test pins the invariant (default fg == theme text; dropping the background releases both defaults). Suite 1344✓, typecheck/lint/prettier clean.
…verlay # Conflicts: # tools/computer_use/cua_backend.py
The usage gauge is Nous subscription-cap-only (used_fraction requires a cap; non-Nous providers emit no headers, so no notice fires). A bare percentage implied a universal unit that doesn't exist, so report the absolute dollars used of the cap instead: used = cap - remaining, from micros (money-safe), clamped to [0, cap]. Still a snapshot at band-crossing (re-emits on band change, not every turn) to keep the single escalating line and stay quiet on append-only surfaces (messaging pushes one message per crossing).
… detail Three fixes to how agent credit notices render as toasts: - Strip the leading severity glyph (the toast already draws a kind icon, so the raw text doubled it). Native OS notifications keep the glyph (no icon there). - Icon top-margin is now 0.42ch (font-relative) instead of a fixed rem. - Band-color the $used figure (semibold) by $used/$cap: muted <75%, --ui-orange >=75%, --ui-red >=90% (depleted red, restored green), reusing the existing --ui-* usage palette. Icon shares the accent. - Split a trailing '. detail' into a muted secondary line (title+description convention) instead of an inline middot. Generic 'accentColor' + 'meta' slots on the notification; degrades gracefully when a notice has no figure.
…esync) The x-nous-credits-* headers are best-effort and can drift out of sync, notably in team/org accounts where another member's spend moves the shared balance without touching this client's headers. The billing endpoint is the source of truth, so the page no longer trusts a cache: staleTime 0 + refetchOnMount 'always' force a fresh fetch on every open and focus (still polling 30s while mounted). The credits.* invalidation nudge still pulls a crossing in immediately.
Ctrl+Shift+C (and window.__creditsDemo()) steps the full credit-notice lifecycle (usage 50->75->90, grant-spent, depleted/restored) through the real gateway event fan-out via a new emitLocalGatewayEvent, so the toast/native/ billing-invalidation paths are testable without hitting real usage bands. Installed only under import.meta.env.DEV, so it's tree-shaken from production.
… (NousResearch#69828) * fix(desktop): render agent credit notices as toasts (NousResearch#69808) The desktop renderer had no handler for the `notification.show` / `notification.clear` WS events, so every credit-usage notice the backend sends (`agent/credits_tracker.py` → `tui_gateway/server.py`) was silently dropped. Credit warnings like "• Credits 50% used · $220.00 cap" never appeared, even though the Ink TUI renders them in its status bar. Add the two missing branches to the gateway-event dispatcher, delegating to a small, pure-testable module: - `store/agent-notices.ts` — `noticeToToast()` maps a notice to a toast (level → toast kind, sticky → durationMs 0, ttl → ttl_ms), and uses the notice `key` as the toast id. Re-emitting the same key REPLACES the toast, so the credits 50→75→90 line escalates in place instead of stacking, and a key-matched `notification.clear` maps straight to `dismissNotification(key)`. - The notice `text` already carries its own glyph (• ⚠ ✕ ✓), so no toast icon is added. - Notices are account-wide, so the toast shows regardless of which session is focused. The Ink TUI (`ui-tui/src/app/turnController.ts`) is the reference for the latest-wins / sticky-vs-ttl / key-matched-clear behavior. Export `NotificationInput` so the mapping's return type can be named. * feat(desktop): native OS credit alerts + billing-page nudge (NousResearch#69808) Round out the credit-notice handling from the previous commit with the two optional pieces from the issue: - Native OS notification for the urgent pair. `credits.depleted` / `credits.restored` also fire an Electron notification when Hermes is backgrounded, via a new `credits` NativeNotificationKind (the existing five didn't fit) with its own toggle in Settings → Notifications (the panel is data-driven off NATIVE_NOTIFICATION_KINDS, so the toggle and i18n are the only additions). The escalating usage line and grant-spent notice stay in-app toasts only. Dispatch is `global` (account-wide, not session-bound) and gated by the user's prefs + backgrounded check. - Billing-page nudge. A `credits.*` crossing invalidates the `['billing','state']` query so Settings → Billing reflects the change immediately instead of waiting up to 30s for its poll. `nativeNoticeInput()` is a pure mapping (urgent-key gate → native input), unit-tested directly; the gateway-event branch does the localized-title lookup and gated dispatch. i18n added for all four locales. * feat(credits): report $used of $cap instead of % in the usage notice The usage gauge is Nous subscription-cap-only (used_fraction requires a cap; non-Nous providers emit no headers, so no notice fires). A bare percentage implied a universal unit that doesn't exist, so report the absolute dollars used of the cap instead: used = cap - remaining, from micros (money-safe), clamped to [0, cap]. Still a snapshot at band-crossing (re-emits on band change, not every turn) to keep the single escalating line and stay quiet on append-only surfaces (messaging pushes one message per crossing). * fix(desktop): de-dupe credit toast icon, band-color the figure, split detail Three fixes to how agent credit notices render as toasts: - Strip the leading severity glyph (the toast already draws a kind icon, so the raw text doubled it). Native OS notifications keep the glyph (no icon there). - Icon top-margin is now 0.42ch (font-relative) instead of a fixed rem. - Band-color the $used figure (semibold) by $used/$cap: muted <75%, --ui-orange >=75%, --ui-red >=90% (depleted red, restored green), reusing the existing --ui-* usage palette. Icon shares the accent. - Split a trailing '. detail' into a muted secondary line (title+description convention) instead of an inline middot. Generic 'accentColor' + 'meta' slots on the notification; degrades gracefully when a notice has no figure. * fix(desktop): billing page always fetches fresh state (team-account desync) The x-nous-credits-* headers are best-effort and can drift out of sync, notably in team/org accounts where another member's spend moves the shared balance without touching this client's headers. The billing endpoint is the source of truth, so the page no longer trusts a cache: staleTime 0 + refetchOnMount 'always' force a fresh fetch on every open and focus (still polling 30s while mounted). The credits.* invalidation nudge still pulls a crossing in immediately. * chore(desktop): dev-only credit-notice demo hotkey Ctrl+Shift+C (and window.__creditsDemo()) steps the full credit-notice lifecycle (usage 50->75->90, grant-spent, depleted/restored) through the real gateway event fan-out via a new emitLocalGatewayEvent, so the toast/native/ billing-invalidation paths are testable without hitting real usage bands. Installed only under import.meta.env.DEV, so it's tree-shaken from production.
…841-no-overlay fix(computer_use): disable cua-driver overlay by default on macOS/WSL (supersedes NousResearch#53841)
# Conflicts: # hermes_cli/config.py # tools/computer_use/cua_backend.py
…se-perf perf(computer_use): cap capture size and cache vision routing
…sh held narration
Two fixes for desktop hands-free voice:
- The live speech session bound to the first assistant bubble with text, so
a tool-calling turn spoke only the opening narration and silently dropped
every later interim AND the final answer. The conversation selector now
aggregates all unspoken assistant bubbles in order (turn-scoped speech);
auto-speak keeps its latest-reply-only behavior.
- The speak-stream WS producer blocked forever on the text queue, so a
narration line with no trailing whitespace ("Let me check.") sat in the
sentence chunker until end-of-turn — spoken long after the tool finished,
with the UI stuck on "Preparing audio…". Mirror the CLI speaker's idle
flush: sentence-terminated buffers flush after 0.5s of producer silence,
anything else after ~2s; open <think> blocks are never flushed.
…k-whole-turn fix(voice): speak the whole desktop turn and idle-flush held narration
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Park the live primary gateway socket on Fast Refresh dispose and re-adopt it on remount so dev UI edits don't tear down the WebSocket. Hold gateway store singletons on globalThis + self-accept HMR on store/gateway.ts. Prod strips import.meta.hot — live unmount unchanged.
Guard the globalThis gateway-state container and the survivor park/adopt calls on the import.meta.hot literal (not the runtime hmrActive() helper), so Vite dead-code-eliminates every HMR path in production. Prod now uses a plain module-local singleton — no globalThis, no Symbol.for — and the survivor module drops out of the bundle entirely. Verified: gatewayRegistryState, gatewaySurvivor, and import.meta.hot are all absent from the prod build. Removes the now-unused hmrActive() export.
Vitest keeps import.meta.hot truthy, so boot-effect cleanup parks the open socket; drain it between cases so the next test boots fresh.
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…v-fixes fix(desktop): keep gateway session alive across Vite HMR
On vulnerable SQLite (e.g. 3.50.4), do not enable WAL for fresh/non-WAL shared databases — prefer DELETE instead. Leave existing on-disk WAL alone (no live downgrade under concurrent gateway/cron openers). Surface Python/SQLite version details as a doctor warning (NousResearch#69784).
Assert the version matrix, fresh-DB DELETE fallback, already-WAL left alone (no checkpoint/DELETE), fixed-SQLite WAL path, and warn-only doctor output for vulnerable builds (NousResearch#69784).
Consolidate the two near-identical warning strings in _log_wal_reset_bug_once into a single logger.warning call with an action variable. Remove overengineered defensive tuple-length handling in is_sqlite_wal_reset_vulnerable (sqlite3.sqlite_version_info always returns a 3-tuple). Remove extra blank line. Follow-up cleanup for PR NousResearch#69981.
Hoist the duplicated check_info(source_id) call out of both if/else branches into a single call after the branch. Remove trailing whitespace on the blank line after the except block. Follow-up cleanup for PR NousResearch#69981.
…l early returns Invalid-tool exhaustion and truncated-tool early returns skipped finalize_turn, leaving role=tool transcripts that become tool→user on the next turn for strict providers. Call close_interrupted_tool_sequence before persist on those paths (same as interrupt aborts).
…d 'always active'
The 'Built-in: always active' label was a hardcoded string that never
reflected the user's actual configuration. It now shows three separate
indicators, each reading from the real source of truth:
- Memory injection: reads memory.memory_enabled from config.yaml
- User profile: reads memory.user_profile_enabled from config.yaml
- Memory tool: checks if 'memory' is in platform_toolsets.cli
(or defaults to enabled if no explicit list)
Before:
Built-in: always active
After:
Built-in (MEMORY.md / USER.md):
Memory injection: disabled ✗
User profile: disabled ✗
Memory tool: disabled ✗
Add tests/hermes_cli/test_memory_status.py with 11 tests covering: - No hardcoded 'always active' label - memory_enabled, user_profile_enabled, memory toolset indicators - Tool enabled/disabled via platform_toolsets.cli - Provider still shown alongside indicators Add huajiang@tubi.tv → thirstycrow to AUTHOR_MAP (PR NousResearch#23630 salvage).
The PR's inline toolset resolution (checking 'memory' in cli_toolsets list) produced wrong results for composite toolsets like 'hermes-cli' which expand to include the memory tool. Replace with the canonical _get_platform_tools() from tools_config.py which correctly handles composite toolsets and all edge cases. Update tests to mock _get_platform_tools instead of raw config.
Add software-development/7-agent-pipeline skill documenting the 7-agent collaborative pipeline for reliable AI feature development: 1. Researcher (read-only) — maps codebase, finds patterns/risks 2. Story Writer — rough idea → user story + acceptance criteria 3. Project Manager — story → technical blueprint 4. Backend Engineer — builds API/services/DB (backend-only) 5. Frontend Engineer — builds UI (frontend-only, parallel with #4) 6. E2E Test Verifier — end-to-end flow tests 7. Validator — reads original story + spec + code, gates merge Source: https://youtube.com/shorts/CVtd7Me_uP4 Root insight: one agent doing 6 roles at once causes cascading wrong assumptions that spread through the codebase undetected.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
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.
No description provided.