merge: upstream/main v0.20 lineage into fork (DAN-2485 base) - #146
Merged
Conversation
…-bkstock chore: contributor email mapping for BKStock
…agmas Addresses review from @teknium1 on PR NousResearch#71755: - Extended apply_database_pragmas() to handle cache_size, mmap_size, and temp_store from config.yaml (alongside existing wal_autocheckpoint and journal_size_limit). No hardcoded defaults — all values are opt-in via config.yaml, avoiding policy conflicts with other PRs. - Applied to ALL connection types: writer (_connect_and_init), read_only cross-profile attach, and WAL per-thread readers (_get_read_conn). Previously PRAGMAs only ran on the writer path. - Removed inline PRAGMAs from _connect_and_init — single source of truth in apply_database_pragmas(). - Documented config keys with examples in function docstring.
…ection types E2E guard for the salvaged PR NousResearch#71755: database.cache_size/mmap_size/ temp_store from config.yaml must reach the writer connection, the read-only cross-profile attach, and the WAL per-thread reader — and a default install (no database: keys) must keep byte-identical SQLite defaults on every connection type. Also covers integer-coercion rejection of garbage values for the three new keys. cache_size uses -16000 (not the doc example -2000) because -2000 is SQLite's compiled-in default and would not discriminate a regression.
… and floating pet Partial pick of the surviving renderer hunks from NousResearch#75395 (perf commit 6502e44 plus fixup 3fbbc9c): gate the 500ms subagent now-ticker and the cron sidebar 1s ticker/run-poll on usePaneVisible, and skip the legacy floating-pet poll while the document is hidden. Dropped hunks (electron/main.ts, vitest.setup.ts/config) intentionally excluded.
…y folds) Two findings from the simplify pass on the final trio diff: - status-pulse: one pause controller + one aligned period timer shared by all StatusPulse instances (ref-counted), instead of N x (document/window/bridge listeners + unsynchronized 5s wakes) — a sidebar can show dozens of pulsing dots. Pause still cancels in-flight animations so the compositor sleeps immediately. - cron-jobs-section: the runs-peek effect created its interval even while the pane was hidden (callback no-oped but the timer still woke the renderer every 8s/60s per expanded job). Early-return when hidden — visibility is already in the dep array, so becoming visible restarts load + timer.
… effect CI-caught: cron-jobs-section had an extra blank line between sorted imports; use-message-stream's visibility-flush effect assigns flushHandleRef.current=null inside a useEffect (legitimate timer-clear, not an atom mirror) — eslint-disable-next-line per the rule's documented convention.
Retarget NousResearch#73639 onto the SessionDB mixin split (hermes_state_common / hermes_state_schema). Fresh installs create UPDATE OF content/tool_* triggers; existing broad AFTER UPDATE triggers are inspected and replaced under schema init without an FTS rebuild (WHEN clauses already guarded content correctness; OF skips non-content status writes that saturated disk I/O on large state.db). Tests: tests/test_fts_update_of_narrowing.py (4)
_ensure_fts_cjk_schema never raises on OperationalError; post-condition after dropping messages_fts_cjk_update now requires a narrowed UPDATE trigger or durable fts_cjk_stale + unavailable. Covers the production soft-fail path the raise-only handler missed.
Simplify-pass fold: to_drop names come from the literal update_names\nallowlist via IN binding, so the [A-Za-z0-9_]+ fullmatch could never\nfail — and if it somehow did, its `continue` would miscount (the\nskipped trigger stayed in len(to_drop)/the log while CREATE TRIGGER\nIF NOT EXISTS silently kept the broad variant). Delete the guard and\nits function-local re import; keep the invariant as a comment.
## Summary - Normalize OpenViking endpoints through `is_always_blocked_url` and fall back to the default local endpoint when poisoned. - Keep intentional loopback / LAN self-host working. - Add focused unit tests. ## Salvage / credit Memory-provider endpoint floor sibling of RetainDB/Supermemory always-blocked hardening (avoids over-broad NousResearch#4984-style private-IP bans). (cherry picked from commit 8fa607d)
(cherry picked from commit 389a90b)
`_start_local_openviking_server()` spawned `openviking-server` unconditionally. Both callers — `initialize()` and the runtime unreachable handler — reach it from a health probe, and that probe can time out client-side while the server is up and serving. The spawned process then loses the data-directory lock and exits immediately with `DataDirectoryLocked`; because the probe keeps timing out, the cycle repeats every cooldown window (~5 min observed). The existing 30s `_failed_refresh` cooldown paces the loop but cannot stop it, since it expires while the underlying condition persists. Probe the target host:port before spawning and treat an occupied port as already-started. This guards both call sites at their single convergence point. The probe deliberately tests only that a listener owns the port — enough to know a second server would lose the lock — and says nothing about that listener's health. The parse/probe now precedes the PATH lookup, so a reachable server is reported as running even when `openviking-server` is not on PATH. Fixes NousResearch#74846 (cherry picked from commit b49427d)
The provider used to disable OpenViking permanently when the server was unreachable. That was fixed: `_ensure_client()` now reconnects lazily, with a 30s cooldown gate in `_ensure_client_locked`. Only one of the seven user-facing warnings was updated to match. The other six still told the user memory was "disabled for this Hermes run", which is no longer true — every one of those paths is retried on the next access. A user who reads the old message has no reason to retry, which is very likely how NousResearch#5721 ("never recovers") came to be filed against behaviour that already recovers. All six sites were traced to confirm none is terminal for the run: the `initialize()`-time and waiter-thread failures never arm `_failed_refresh` (only line 2439 does), so they retry on the very next access with no cooldown at all. The replacement wording deliberately omits the "(after cooldown)" parenthetical used at the already-correct site — that detail is only accurate where `_failed_refresh` was just armed. The neutral phrasing is true at all six. Also promotes two clause separators to periods to avoid "…; …disabled;" collisions. (cherry picked from commit 8346403)
`_committed_session_ids` is a permanent per-sid latch, and `_session_needs_commit` checks it before the turn counter by design — a racing sync_turn can re-increment `_turn_count` after commit+reset, so the guard must win to stop a double-commit. That is correct for a session being left behind. It is wrong for one that keeps its id. `compress_context()` commits before rewriting the transcript in both modes, and with `compression.in_place: true` (the default) `on_session_switch` receives the same id and does not rotate. The latch then rejects every later commit for a still-live session — the next compression, /new, normal session end, startup recovery — so every post-compression turn is silently never extracted. Rotation mode is unaffected because a fresh child id is minted and starts clean, which is what confirms the latch's intent was only ever to dedupe the departing id. Clear the latch when compression completes without rotation. Turns arriving after that point are genuinely new, and this is a defined moment rather than a race. The rotation path is untouched, so the old id stays latched and its _finalize_session_async still dedupes against the compression commit. Fixes NousResearch#74695 (cherry picked from commit d1e5c3d)
Review feedback: the previous test called _mark_session_committed directly, so it verified the guard's behavior but not the wiring that sets it — a future break in the commit_memory_session -> same-id compression-boundary path would not be caught. Add a lifecycle regression that drives the real sequence: on_session_end commits through the actual path, on_session_switch(same id, reason="compression") crosses the boundary, sync_turn records a genuinely new turn, and a second on_session_end must produce a second commit POST. Without the fix it fails showing exactly one commit call, which is the reported data loss: every turn after the first compression is dropped. The rotation and /undo tests stay as scope guards. (cherry picked from commit 0ca5a33)
…nViking and RetainDB OpenViking is_available() only consulted env vars and use_ovcli_config, so an endpoint saved to config.yaml (e.g. by the Dashboard) reported needs_config; _resolve_connection_settings() likewise never folded config.yaml's non-secret fields into its chain. RetainDB initialize() read base_url/project from the environment only, ignoring the values the Dashboard writes to config.yaml. Both now resolve non-secret fields as env -> (ovcli ->) config.yaml -> default; secrets still come from the environment. Adds regression tests for both. Fixes NousResearch#68209 (cherry picked from commit dca57915b97b5705b30927a062e1d0f2f23d3841)
…s as fallback _recall_config() previously read all settings (recall_limit, score_threshold, recall_resources, etc.) exclusively from environment variables. This forced users to store behavioural configuration in .env, violating the Hermes convention that .env is for secrets only. The infrastructure to load config.yaml -> memory.openviking was already in place via _load_hermes_openviking_config(), but _recall_config() never called it. Fix: call _load_hermes_openviking_config() and pass its values as the default parameter to _env_int/_env_float/_env_bool. Env vars still override config.yaml values, preserving backward compatibility. Closes NousResearch#62540 (cherry picked from commit 6aadf12)
…HOME tests Add three tests to TestOpenVikingConfigSchema: 1. test_recall_config_reads_from_config_yaml — writes memory.openviking settings in config.yaml and verifies _recall_config() consumes them. 2. test_recall_config_env_overrides_config_yaml — writes both config.yaml and OPENVIKING_RECALL_* env vars, verifies env takes precedence. 3. test_recall_config_partial_config_yaml — partially populated config.yaml falls back to defaults for omitted keys. All 46 openviking_plugin tests pass (43 existing + 3 new). (cherry picked from commit b8d7834)
Review follow-up for salvaged PR NousResearch#76782. Three setup-wizard validation functions called _normalize_openviking_url outside their try/except blocks. Since _normalize_openviking_url now raises _OpenVikingEndpointError for blocked or malformed endpoints, an invalid endpoint would crash the wizard instead of returning a friendly (False, message) tuple. - _validate_openviking_auth: move _normalize_openviking_url inside try - _validate_openviking_root_access: same - _validate_openviking_setup_values: catch _OpenVikingEndpointError explicitly - Remove dead ternary in _normalize_openviking_url safety check (candidate always has http/https scheme by that point) - Replace redundant float('-inf') < x < float('inf') with math.isfinite() in _setting_float; drop the redundant infinity check from _setting_int (is_integer() already rejects inf/nan)
The original PR NousResearch#78453 said 'A job that was mid-run during a restart resumes according to the attempt policy described in this page.' This is misleading — the existing docs explicitly state 'Unknown attempts are audit records and are never automatically rerun.' Corrected to accurately describe: the mid-run attempt is marked unknown (not retried), but the job's next scheduled tick fires normally.
Remote headless backends have no PortAudio mic, so "hey hermes" fails even when openWakeWord is installed. Let the desktop stream 16 kHz int16 PCM via wake.feed while detection stays server-side. - wake_word.capture: auto|local|client (+ GUI client_capture prefer) - WakeWordDetector external_audio queue + feed_audio API - wake.feed RPC; wake.start/status report capture + frame_length - Desktop getUserMedia feeder; stop on wake.detected, re-arm after voice - Docs + unit tests (26 pass in tests/tools/test_wake_word.py)
- wake.status reports effective capture from the armed detector (client vs local), plus frame_length/sample_rate; GUI status probes prefer client - Gateway test doubles accept external_audio on start_listening - Desktop PCM feeder uses a bounded ordered queue instead of dropping frames while a wake.feed RPC is in flight - /wake on and status/re-arm paths pass client_capture so remote reattach works
With capture:auto the desktop always preferred client streaming, so a local desktop with a working backend mic silently switched from PortAudio to getUserMedia default-device — dropping wake_word.input_device selection (NousResearch#74363). A ready backend input now wins under auto; client capture is the fallback for a preferring surface on a mic-less backend, and capture:client still forces streaming. Also removes the dead auto branch (both arms returned local) and lets the client-feed test skip cleanly when numpy is absent.
Sending one 80 ms frame per RPC is ~12.5 gateway calls/s for as long as the ear is armed. Drain up to 4 queued frames into a single wake.feed payload (backend feed() already splits long buffers into engine frames) — ~3 RPCs/s steady-state. Fix the wake.feed size-cap comment (64000 bytes = 2 s, not 0.5 s).
* fix(desktop): preview remote HTML over SSH * fix(desktop): harden remote HTML sanitization
A plain click on a composer file row in remote mode handed the backend's file:// URL to the local browser bridge, which cannot resolve a path that only exists on the gateway host. Route remote non-HTML file targets to the gateway-backed in-app preview pane instead; local files, ordinary URLs, and remote HTML (staged locally by openPreviewTargetInBrowser) keep their existing browser path. Supersedes NousResearch#70296 and NousResearch#57878. Co-authored-by: lesterlxt <153183032+lesterlxt@users.noreply.github.com> Co-authored-by: cj52973 <cjenkins@scacpa.org>
- perfectionist/sort-imports in store/wake-word.ts - contributors/emails mapping for drew@kainotomic.com -> appletechie
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…ch#79494) A plain click on a composer file row in remote mode handed the backend's file:// URL to the local browser bridge, which cannot resolve a path that only exists on the gateway host. Route remote non-HTML file targets to the gateway-backed in-app preview pane instead; local files, ordinary URLs, and remote HTML (staged locally by openPreviewTargetInBrowser) keep their existing browser path. Supersedes NousResearch#70296 and NousResearch#57878. Co-authored-by: lesterlxt <153183032+lesterlxt@users.noreply.github.com> Co-authored-by: cj52973 <cjenkins@scacpa.org>
…7705) * feat(desktop): shared pane-strip primitives — one bar, one glyph, one close menu The zone header hand-rolled its tab bar, its close-verb context menu, and the bare-glyph "+" inline; the preview rail kept a second copy of all three. Extract PaneTabStrip (the bar), PaneStripGlyph/PaneStripTool (glyph buttons as data, titlebar-tool style), and paneTabCloseItems (the four close verbs) into the pane-tab primitives, and render the zone header through them. Panes contribute strip glyphs via PaneChrome.stripTools; $stripToolsRevision tells the strip to re-read. * refactor(desktop): preview tabs are layout-tree tiles like session and page tiles The in-app browser / preview rail carried its own tab strip beside the zone's own — a second bar at a different height with its own close menu, label casing, ⌘W rung, and welded to the file browser's zone so ⌘J toggled it away. It predated the layout tree. $previewTabs now mirrors into pane contributions through the same paneMirror session and route tiles use, so a preview tab IS a zone tab: one strip, drag/ stack/split, the shared close verbs, plain ⌘W, its own zone docked beside main. URL tabs are titled Browser (the tab names the surface, not the page); files keep their filename and a file-type lead glyph. Deleted with the rail: the preview pane contribution + PREVIEW_PANE_ID + its visibility binding, the 'preview' placements in the default tree and presets, the ⌘W rail rung, the reveal listener, and the preview.close* i18n keys (copies of zones.*). lone-header now keys on "closeable placement:main" instead of the session-tile: id prefix, so any tile dragged into its own zone keeps its tab. * fix(desktop): preview console/DevTools live on the strip, and DevTools tells the truth The two toggles were titlebar tools — far from the preview they act on and one ambiguous global pair once two previews were open. They're strip glyphs now, contributed per-tab as PaneStripTool data with real tooltips: the console store is cached by tab id so the glyph and the panel read the same logs, and the pane registers a DevTools handle for its tab. DevTools active state was also a lie: it tracked our click handler, so closing the DevTools window itself left the glyph stuck on. The webview's devtools-opened/closed events drive it now. * fix(desktop): ⌘W and ⌃Tab work over preview and page zones The generic tab verbs keyed zone eligibility on the CHAT strip (workspace / session-tile: ids), so a zone holding only a Browser or page tile was invisible to them: ⌃Tab skipped it, and ⌘W fell through the chat rung and emptied the MAIN chat while you were looking at a preview. ⌘1…⌘9 already worked — the verbs disagreed about what counts as a tab strip. New isMainStripPane (any placement:'main' tenant — sessions, pages, previews) drives ⌘W and ⌃Tab; isSessionStripPane keeps gating what it should: where a session may dock (⌘T's anchor, the strip's +). * fix(desktop): preview tab selection follows the tree, not just the reverse openPreview drove tree reveals, but clicking a preview TAB only activated its pane in the tree — $rightRailActiveTabId kept naming the previous tab, so $previewTarget (⌘L quote labels, the titlebar's has-preview state) reported a tab that wasn't on screen. The mirror now also listens tree→store: when the interacted zone's active pane is a preview tile, the store selection follows. Both directions converge on the same id, so no ping-pong. * fix(desktop): session drags land in preview and page zones tileZoneHost replaces chatZonePane: a zone hosting any main tile (a Browser tile, a page) accepts stack and split drops — the known asymmetry where you could drag a preview tab out but never drag a session in. Only a CHAT zone's center is the link-to-composer drop; a preview zone's center stacks, since there's no composer to link to. * chore(desktop): drop the rail's dead multi-close verbs closeActiveRightRailTab / closeOtherRightRailTabs / closeRightRailTabsToRight lost their last callers when ⌘W and the close menu moved to the zone strip's shared rungs; the tests now exercise closeRightRailTab's own fallback behavior directly. * fix(desktop): open_preview lands whenever its session is on screen The preview.open handler honored the event only when its session was the FOCUSED one — but the turn that runs open_preview is usually a tile's session, and by the time the tool fires the user's last click has often parked focus on main (or anywhere else). The tool reported success, the store never wrote, and nothing appeared: an explicit 'open reddit' silently vanished. On-screen is the right bar: honor the open when the session is the primary chat or any open tile, which keeps truly invisible background sessions from yanking the pane (offer, don't hijack) without eating opens the user asked for. * fix(desktop): one Browser — a second URL navigates it, not a second tab Tabs were keyed url:<address>, so every distinct page the agent opened stacked another BROWSER tab — three opens, three Browsers, each titled identically because the tab deliberately names the surface, not the page. The title already said singleton; the key disagreed. URL targets now share one url:browser id: openPreview re-fronts the tab and swaps its target, and the pane rebuilds its webview against the new address. Files and artifacts keep per-identity tabs. Restored storage rekeys old per-address rows and keeps only the most recent.
The GUI now passes client_capture: true on wake.start, wake.status, and the post-voice re-arm; update the store and slash-handler tests to the new param shape. 123/123 pass locally.
PDFs were classified as generic binary/text previews, rendering raw %PDF bytes locally and failing entirely for remote-only files. Classify PDFs as their own preview kind, load bytes through the existing local/remote filesystem bridge, convert them to revocable Blob URLs for Chromium's embedded viewer, migrate persisted pre-PDF tabs at restore, and retry restored previews when the active filesystem connection changes. Salvaged from NousResearch#76008-era base onto current main: PDF classification now composes with the remote-HTML enrichment branch, and the persisted-tab migration runs before the One-Browser URL rekey in decodePreviewTabs. Supersedes NousResearch#76565. Co-authored-by: Brooklyn Nicholson <brooklyn@brooklyn.sh>
…reaming (NousResearch#79491) * feat(wake): client-capture wake word for remote desktop Remote headless backends have no PortAudio mic, so "hey hermes" fails even when openWakeWord is installed. Let the desktop stream 16 kHz int16 PCM via wake.feed while detection stays server-side. - wake_word.capture: auto|local|client (+ GUI client_capture prefer) - WakeWordDetector external_audio queue + feed_audio API - wake.feed RPC; wake.start/status report capture + frame_length - Desktop getUserMedia feeder; stop on wake.detected, re-arm after voice - Docs + unit tests (26 pass in tests/tools/test_wake_word.py) * fix(wake): address review on client-capture re-arm and feed queue - wake.status reports effective capture from the armed detector (client vs local), plus frame_length/sample_rate; GUI status probes prefer client - Gateway test doubles accept external_audio on start_listening - Desktop PCM feeder uses a bounded ordered queue instead of dropping frames while a wake.feed RPC is in flight - /wake on and status/re-arm paths pass client_capture so remote reattach works * fix(wake): auto capture keeps the backend mic when one exists With capture:auto the desktop always preferred client streaming, so a local desktop with a working backend mic silently switched from PortAudio to getUserMedia default-device — dropping wake_word.input_device selection (NousResearch#74363). A ready backend input now wins under auto; client capture is the fallback for a preferring surface on a mic-less backend, and capture:client still forces streaming. Also removes the dead auto branch (both arms returned local) and lets the client-feed test skip cleanly when numpy is absent. * perf(desktop): coalesce wake.feed frames Sending one 80 ms frame per RPC is ~12.5 gateway calls/s for as long as the ear is armed. Drain up to 4 queued frames into a single wake.feed payload (backend feed() already splits long buffers into engine frames) — ~3 RPCs/s steady-state. Fix the wake.feed size-cap comment (64000 bytes = 2 s, not 0.5 s). * docs(config): document wake_word.capture in cli-config.yaml.example --------- Co-authored-by: Andrew <drew@kainotomic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
The dashboard now mints an action_id per backend update, hands it to the spawned `hermes update` via HERMES_ACTION_ID, and reuses an in-flight update action instead of spawning a duplicate. The updater prints a bounded `=== hermes-update completed <id> ===` receipt on every success path — normal, zip, dependency-repair, and the no-op "Already up to date!" path that previously ended with no terminal marker at all (NousResearch#58764) — so the Desktop can prove completion across the dashboard restart boundary instead of guessing from stale log text. Co-authored-by: Vitor Cepeda Lopes <vitor@vitorcepedalopes.com> Co-authored-by: doncazper <caztronics@yahoo.com>
Remote backend updates failed with "Backend update failed." on nearly every run: applyBackendUpdate() polled for only 30×1.5s ≈ 45s, then read exit_code null off the still-running action and called it a failure. Real updates (backup + uv sync + npm install + vite build) routinely run longer, and the no-op "Already up to date" path never restarted the gateway so the old return-check timed out too. A still-running, reachable action is now never converted into failure by an elapsed budget — only a nonzero exit is. The apply loop keeps one in-flight promise, tolerates reconnects during the dashboard restart without extending the fixed six-minute deadline forever, and confirms success by the action-specific receipt that survives the restart, falling back to proving the requested commit / up-to-date check for older backends without action_id support. Inconclusive completion fails closed. Fixes NousResearch#47359 Fixes NousResearch#58764 Co-authored-by: Vitor Cepeda Lopes <vitor@vitorcepedalopes.com> Co-authored-by: Mark Vlcek <markvlcek@gmail.com> Co-authored-by: doncazper <caztronics@yahoo.com>
…-preview fix(desktop): render remote PDFs in preview rail
* feat(agent): read_preview — the desktop-gated tool that reads the in-app browser The agent could open the preview pane (open_preview) and read the embedded terminal (read_terminal), but the browser it had just opened was a black box — 'what does this page say?' had no answer. read_preview mirrors read_terminal end to end: HERMES_DESKTOP-gated via check_fn (zero schema footprint outside the GUI), dispatched through the same agent callback pattern, windowed with start/count so a long page pages instead of flooding context. * feat(gateway): preview.read blocking bridge Same lifecycle as terminal.read: the tool blocks on preview.read.request, the renderer answers preview.read.respond (allow_expired — a slow page extraction losing the 45s race must not surface a raw 4009), and a timeout emits preview.read.expire so late answers resolve quietly. * feat(desktop): the renderer serializes the active preview tab for the agent preview-reader.ts is the preview analog of the terminal's buffer registry: the URL pane registers a page reader (webview executeJavaScript → title + visible innerText) keyed by tab id; readActivePreview resolves the ACTIVE tab, windows the text (24k cap per read), and answers file/artifact tabs with identity plus a note pointing at the tool that reads that content directly. The gateway event handler answers preview.read.request beside terminal.read.request.
…tes (NousResearch#79513) * feat(update): emit an action-scoped terminal receipt from hermes update The dashboard now mints an action_id per backend update, hands it to the spawned `hermes update` via HERMES_ACTION_ID, and reuses an in-flight update action instead of spawning a duplicate. The updater prints a bounded `=== hermes-update completed <id> ===` receipt on every success path — normal, zip, dependency-repair, and the no-op "Already up to date!" path that previously ended with no terminal marker at all (NousResearch#58764) — so the Desktop can prove completion across the dashboard restart boundary instead of guessing from stale log text. Co-authored-by: Vitor Cepeda Lopes <vitor@vitorcepedalopes.com> Co-authored-by: doncazper <caztronics@yahoo.com> * fix(desktop): make remote backend updates terminal-state driven Remote backend updates failed with "Backend update failed." on nearly every run: applyBackendUpdate() polled for only 30×1.5s ≈ 45s, then read exit_code null off the still-running action and called it a failure. Real updates (backup + uv sync + npm install + vite build) routinely run longer, and the no-op "Already up to date" path never restarted the gateway so the old return-check timed out too. A still-running, reachable action is now never converted into failure by an elapsed budget — only a nonzero exit is. The apply loop keeps one in-flight promise, tolerates reconnects during the dashboard restart without extending the fixed six-minute deadline forever, and confirms success by the action-specific receipt that survives the restart, falling back to proving the requested commit / up-to-date check for older backends without action_id support. Inconclusive completion fails closed. Fixes NousResearch#47359 Fixes NousResearch#58764 Co-authored-by: Vitor Cepeda Lopes <vitor@vitorcepedalopes.com> Co-authored-by: Mark Vlcek <markvlcek@gmail.com> Co-authored-by: doncazper <caztronics@yahoo.com> --------- Co-authored-by: Vitor Cepeda Lopes <vitor@vitorcepedalopes.com> Co-authored-by: doncazper <caztronics@yahoo.com> Co-authored-by: Mark Vlcek <markvlcek@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…lay-skill-metrics feat(observability): aggregate bounded skill metrics
Strategy: -X theirs (upstream wins all conflicts). Dan's custom fork commits (memgw provider, slack wiring, email HTML, cbm hooks, CI workflows, security redactions) cherry-picked back in follow-ups. Deps bumps left to upstream. # Conflicts: # acp_registry/agent.json # gateway/platforms/slack.py # gateway/platforms/telegram.py # tests/cron/test_cron_profile.py # ui-tui/package-lock.json # ui-tui/packages/hermes-ink/package-lock.json # web/package-lock.json
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
dizhaky
added a commit
that referenced
this pull request
Aug 5, 2026
16 conflicts resolved by taking sync-branch (v0.20) version: - 8x shared_metrics/relay_shared_metrics observability (add/add) - uv.lock (clean v0.20 lockfile; main's had duplicate-key bug from #146) - package-lock.json x2, ui-tui/package.json, apps/desktop/package.json - 3x test files + 1 smoke script uv lock --check passes (252 packages, 0 duplicate keys). Co-Authored-By: Claude <noreply@anthropic.com>
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.
Base sync: upstream/main merged with -X theirs + upstream deletions accepted. Dan's custom features survive except conflicted hunks (recovery via cluster agents next). Gates native Hermes Desktop auth (v0.17 needs upstream-era backend).
Context: fork was 136 ahead / 11,483 behind upstream (merge-base 2026-05-23). 7,372 files changed.
DO NOT auto-merge — CI must pass + recovery commit follows.
Refs DAN-2485
🤖 Generated with Claude Code