Keep panel tabs in the titlebar moved those app actions next to the
sidebar toggle, which ate the tab strip. Put them back on the right
edge. Sidebar toggle stays left. Fixes#107351.
(cherry picked from commit 7f4460a7f6028cf384506733a5bfa52273792d64)
`revealDesktopPane` drove files/review/sessions/terminal through their
store setters only. Those are same-value no-ops when the pane's `$open`
already reads true while the user minimized its zone from the header
chevron, so the `focus_pane` tool reported success over an invisible
pane (#106009; class noted by @worryfreeaa). Route every tree-backed
revealer through `revealTreePane` after its own setter, which clears
`minimized` and fronts the pane.
(cherry picked from commit 690a1a75108ec63ce5cb1a638543969b0877dbaa)
* fix(desktop): centralize guide handoff receipt reads
Resolve the guide receipt key and value together in setup-profile. Use the helper at all four read sites so connection scoping follows one implementation.
* fix(desktop): recover from unreadable handoff receipts
Memoize receipt reads and show Retry only for the error phase. Quarantine corrupt data before retrying, and resolve the guide identity when the failed request did not retain it so a fresh build can start.
Cover preservation of corrupt data and removal from the active receipt key with an invariant test.
* fix(desktop): validate persisted onboarding phases from one list
Derive OnboardingPhase and persisted-value validation from the same phase list so future phases survive relaunch. Verify every persisted phase reloads and an unknown value falls back to idle.
* fix(desktop): share window centering arithmetic
Extract centeredBounds and use it for onboarding boot and window growth. Keep the existing work-area clamps and coordinate rounding unchanged.
* fix(desktop): compute progress steps inline
Remove the ineffective ProgressCard memo because streaming flushes replace the messages array. Keep the same transcript scan and rendered steps.
* fix(desktop): center the free-tier status chip detail
Wrap the model label and sign-in badge in an inline flex span with a shared gap. This centers the badge beside the model text without changing other status-bar details.
* fix(desktop): derive the guide receipt key in one place
The Retry path spelled the key derivation out again because the read helper throws on a corrupt receipt before it can return the key. A separate guideHandoffReceiptKey serves both the reader and the quarantine, so the derivation has one home again.
* fix(desktop): keep the free-tier badge at its intended leading
Badge declares leading-none, but the class merger drops it behind the size variant's font-size class, so the badge inherits a 1.5 leading and renders 16px tall next to an 11px label. That height, not the inline alignment, is what read as a detached badge. Restating leading-none on the chip's badge brings it to 11.6px, inside the label's cap height. The Badge component itself is left alone; every other badge in the app has the same dropped leading and that is a separate decision.
- Multi-range requests (any comma) now fall back to a full 200 instead of silently serving only
the first part (RFC 7233 permits ignoring Range); documented + pinned by a test.
- Open the file first and fstat that handle, then stream from the same FileHandle, so
Content-Length and the bytes delivered come from one open file (no stat/stream race).
Handing the FileHandle (not the raw fd) to createReadStream avoids a double close.
- ENOENT/ENOTDIR return a 404 Response instead of rejecting; covered by a test.
The custom setPermissionCheckHandler only allowed media/audioCapture/
videoCapture, which made Electron deny the 'automatic-fullscreen'
permission consulted during HTML5 video requestFullscreen(). The
request handler's isMediaCapturePermission() also returned false for
'fullscreen'. Result: the native fullscreen button on <video controls>
in chat silently did nothing.
Allow 'fullscreen' + 'automatic-fullscreen' in both handlers.
Verified with a minimal Electron repro using Hermes' exact handlers:
requestFullscreen() failed with 'TypeError: Permissions check failed'
before; works after. User-verified in the packaged desktop app.
Let settled remote sessions continue their first quit. Fence late local starts and join existing local and SSH drains without cancelling managed update recovery.
Co-authored-by: Gille <4317663+helix4u@users.noreply.github.com>
Co-authored-by: ChanPark03 <parkchan0302@gmail.com>
Direct chat already uploads PDFs into the session workspace. Group turns
called pdf.attach instead, which needs pdftoppm and swallowed failures, so
bots saw the filename and no file. Stage PDFs the same way as other files,
and name a failed attach in that member's prompt.
Group PDFs currently only hit pdf.attach, so a member prompt can name the
file while the session workspace never receives it. These tests require the
same file.attach + @file: ref path 1:1 chat uses, and a named failure when
that staging throws.
Queue a forced follow-up when Test overlaps an older enumeration; retain bounded caching for incidental focus events.
Co-authored-by: FalconOrtiz <falcon.ortiz11@gmail.com>
Keep a runtime turn descriptor, feed its roster key into row mood and activity filtering, and release only the completing invocation’s presence. Stop uses the captured owner rather than a name lookup.
Co-authored-by: Tuna Dev <tuancookiez@gmail.com>
Preserve drafts and attachments through the existing nothing-sent contract. Port the command-shaped guard to the shared send boundary with behavioral coverage and localized feedback.
Co-authored-by: ClintonEmok <54935030+ClintonEmok@users.noreply.github.com>
#107239 hid the app's fixed titlebar clusters on every contributed full
page, so a plugin route that mounts no titleBar.* content got a bare
strip: sidebar toggle, settings gear, layout editor, HUD, flip and
right-sidebar controls all unmounted, and titleBar.tools items were
dropped with no opt-out on RouteContribution.
The band now yields only while the page actually projects chrome into
it. titleBar.* contributions are mount-scoped, so the presence check
follows the page; a chrome-owning page also keeps its titleBar.tools
items in the band.
useComposerMetrics dedupes its --composer-measured-height writes against
a "last published bucket" ref. The unmount cleanup cleared the surface
vars but left that ref alone, so after a non-final unmount (StrictMode
replay, Suspense hide) the re-mount measured the same dock, saw an
unchanged bucket, and never wrote the var back. The thread then read
the :root estimate (~62px) under a dock that could be 200px tall, and
the status stack covered the last turn until a real resize fired.
Reset the bucket refs in the same cleanup that clears the vars. Adds a
StrictMode regression test that fails on the old code.
* refactor(desktop): compress intro reveal
Remove the unused inline cinematic fallback and its skip callback plumbing now that the native window owns playback. Keep native timing and exit behavior unchanged, colocate the spinner with text effects, and document the current launch and handoff contract.
Area delta against B1: 47 additions, 53 deletions, net -6 lines across six files. Most fork verdicts were already applied in B1.
* refactor(desktop): compress guided chat surface
Remove random greeting variants and retain one existing opener per locale,
while preserving the banked greeting and machine-name suggestion. Trim
assembly commentary while keeping the reasons for its layout invariants.
Move solo-boot and window-growth IPC into a topical Electron sibling so
onboarding handlers no longer grow main.ts. Preserve sender gating,
reveal ordering and window geometry.
* refactor(desktop): compress onboarding handoff
Split welcome-chat kickoff from durable handoff effects and wire each
hook directly. Remove duplicated option types, a redundant readiness
comparison, nullable receipt-key state and stale prose while preserving
B1 routing and recovery.
* refactor(desktop): compress guided chat back half
Remove the duplicate handoff completion key and its reader/writer helpers.
Use the onboarding phase record for replay guards and settled cards while
keeping accepted receipts as the completion boundary.
Preserve the signpost and plugin plan under ruling 5.
* refactor(desktop): compress stores and transcript integration
Remove unused machine reset and untargeted host composer submission. Trim machine and presence commentary while preserving their live consumers. Wire reasoning through the existing scratchpad surface and memoise progress history without mutating it.
Keep parser and connector rendering under rulings 4 and 5. Area delta: 36 insertions, 101 deletions; net -65 lines.
* fix(desktop): keep skipped onboarding apart from a completed handoff
Make skipGuide() persist skipped and let beginOnboardingHandoff accept
guided or skipped. requestSetupHandoff and HandoffCard derive completion
from the accepted receipt.
The latch merge conflated skipping the guide with starting the first
build. A later handoff therefore claimed "was started" without creating
a session. Preserve skipping as its own terminal phase so a later
handoff can create the build and reach done only after acceptance.
* refactor(desktop): B2 review notes
Correct the layout-growth comment in assembly.ts: growing to preserve
the chat size balloons the window. Restore the WHY clauses in
onboarding-handoff.ts and onboarding-kickoff.ts for pending title metadata
on older backends and the caller's requestGateway reading the create pin.
Move guideSourceConnectionId beside $setupSession in setup-profile.ts
and derive each hook's option types from useSessionActions, so kickoff
no longer imports the heavier handoff leg.
Move $handoffError and retrySetupHandoff beside $setupHandoff in
setup-profile.ts and update the card and handoff hook importers.
This removes the setup-profile/handoff-receipt cycle and leaves receipt
persistence dependent only on storage and its receipt type.
* fix(desktop): derive the first-build receipt key one way
Use guideSourceConnectionId(guide.storedId) for the save and request
receipt keys, matching resume and HandoffCard. Keep guide.connectionId
for RPC routing and preserve the receipt key's string format.
At boot the resume path only knows the guide's stored id. When no owner
hint exists but an active gateway connection does, keying writes by the
resolver's ambient route hides the accepted receipt from resume and
leaves the card on Opening. One derivation lets every path find the same
receipt without changing where the build request is sent.
* feat(desktop): intro type at 150% for legibility
Set the intro window's root font size to 150%. Every measure in the intro
is in rem, so the chat card, its rows, bubbles and gaps scale together.
The brand close uses viewport units; its wordmark and tagline are scaled
by hand to match (6.8 to 10.2 vmin, 1.35 to 2 vmin). The hero card's
width cap rises from 900 to 1350 px so lines keep their length on a
large display; its minimum width is unchanged so the three-column stage
still fits a laptop.
The intro fills a display the user sits back from, and at the app's 16 px
root its text read too small on a large monitor (director ruling).
* fix(desktop): status bar keeps one fill under glass; free-tier chip reads Nous, model, Sign in
Under the glass appearance in sidebar scope, the body paints a hard stop
at the rail's edge (glass mix left, opaque chrome right) and the status
bar was transparent, so the seam ran through the bar and cut whichever
item sat on it: in the 886 px guided window, the free-tier chip. The bar
now belongs to the opaque content column across the full width, the way
Finder's does; window scope has no seam and keeps the transparent bar.
The chip itself read "Nous · free tier · nous/welcome" with the Sign in
badge touching the label. It now reads "Nous", the model id small and
monospace, then a solid Sign in badge set off by a gap; the full
"Nous · model" string moves to the tooltip. "Free tier" is no longer
said in the bar (director ruling).
* feat(desktop): port guided onboarding substrate
Add seeded session creation, transcript directives, profile routing, and the shared window and pane primitives needed by the guided flow. Keep later-step mounts deferred and exclude provider selection and retry machinery.
* refactor(desktop): anti-slop cleanup for substrate
Assemble seed parameters in the existing create helper and use the owning transcript attribute type. Read the guaranteed gateway and connection contracts directly to remove runtime type probes and unchecked assertions.
* test(desktop): create-overrides invariants
Verify that reasoning and title overrides do not select a provider or model. Empty overrides and seeds add no parameters.
* feat(desktop): port first-run cinematic window
Play the cinematic behind the guest onboarding launch flag using bundled Collapse and JetBrains Mono. Give the native window its own controller and restore the app on skip, renderer deadman or native watchdog.
Drop the perf scenario because it depends on the removed replay hook. Guided chat kickoff and app-shell gate wiring remain with their later steps.
* refactor(desktop): anti-slop cleanup for cinematic
Preserve audio and canvas behavior through named types and inferred results. Split the viewport node and frame drawing to keep control flow bounded. Cut comments that only repeat the code.
* feat(desktop): add onboarding gate and answers stores
Track cinematic, guided chat, handoff and completion in one phase record. Queue the guide after the intro and share pending kickoff work between callers.
Keep existing saved answers while dropping retired preferences. Leave intro seen-state ownership with the cinematic store.
* feat(desktop): port guided onboarding chat
Add guided setup cards, runbooks, machine context, and onboarding presence. Connect transcript rendering and first-build progress to the desktop behind the onboarding flag. Leave session kickoff and handoff execution for the next step.
* refactor(desktop): anti-slop cleanup for guided chat
Keep directive and layout lookups typed. Remove unsafe test casts and isolate onboarding transcript calculations without changing the flow.
* feat(desktop): connect guided onboarding to durable first-build handoff
Start the guide only after its profile backend confirms bootstrap readiness. Seed or adopt the welcome chat, then transfer the first build to default with a durable receipt and explicit retry.
Wire cinematic completion, screen stand-down, layout growth and progress check-ins. Save agreed preferences before creating the build and release prompt slots after storage refusal.
* refactor(desktop): anti-slop cleanup for onboarding handoff
Reuse the gateway request and error contracts. Isolate guide adoption and snapshot validation while preserving receipt recovery and reasoning overrides.
Validate persisted receipt fields at the JSON boundary without coercion. Keep corrupt identities rejected and retain only the permitted test mocks.
* fix(desktop): guided chat review fixes
Wire the native machine probe so guided setup can suggest a name and offer the right first task. Restore the comments that explain the flow boundaries.
The directive registration uses the launch flag to preserve ordinary chat. Ruling 6 folds active.ts into assembly to keep activity ownership together and removes the second greeting source so the seeded and visible greetings agree.
* fix(desktop): handoff review fixes
Probe the guide backend before switching profiles so a readiness refusal keeps classic onboarding on the current backend.
Restore list-valued personalization coverage and routing rationale. Remove the obsolete setup status fixture.
* chore(desktop): onboarding script cull and rehearsal recipe
Document a temporary-state rehearsal using the existing onboarding flag and optional portal stand-in. Keep the main scripts unchanged and retain window growth for the guided chat.
* fix(connectors): reject incomplete catalog responses
* feat(gateway): scope connector controls to the owning session
* feat(desktop): connect apps through native session-owned controls
* feat(desktop): gate connector cards and enable free-tier access
Use the launch flag before mounting connector controls so classic transcripts add no status requests. Allow existing free-tier identities through the read-only tool gateway gate and test the owning-profile RPC path with A’s launch gate. Keep authorization links out of previews.
* style(desktop): format connector translations
Apply Prettier to the connector copy blocks while preserving upstream translations and free-tier wording.
* refactor(desktop): anti-slop cleanup for connector card
Use the transcript JSON contract and concrete RPC parameters. Preserve malformed-value filtering at one string boundary and make the fixture and row types explicit. Keep connector execution and cancellation behavior unchanged.
* feat(desktop): detect initial language from the OS
Use the native machine locale when no supported language is saved. Preserve explicit choices and leave inferred languages out of config.
* refactor(desktop): anti-slop cleanup for initial locale detection
Keep unvalidated config values at the existing validation boundary. Pass no saved choice after that boundary has ruled it out, preserving locale precedence.
* test(desktop): onboarding port test set
Make native window tests reject duplicate IPC handlers and isolate disabled onboarding. Assert the active gate mock when onboarding re-enables.
Keep the test set limited to behavior carried by the port.
* fix(desktop): recover failed guide kickoff and reveal once
The review found that a failed guide create stranded the solo shell and draft profile, and solo boot faded an already visible window a second time. Restore the prior route and layout, release onboarding through its existing phase record, and surface create failures. Let the film own the reveal while solo boot animates the visible resize.
* fix(desktop): preserve transcript ownership across cards and handoff
The review reproduced answers submitted to the focused chat, repeated questions disabled across sessions, handoff recovery using foreground identity, and mount-dependent progress history. Target each card’s own composer, scope settlement to its message and session, carry the issuing guide through handoff, and derive progress from its transcript with streaming activity. Reuse the existing owner ladder for exact and profile-only routes.
* fix(gateway): preserve connector ownership with profile routing
The review found that shared-primary profile metadata was rejected before connector dispatch, while desktop controls treated a missing registry id as missing ownership. Accept profile only as routing metadata and keep the live transport as authorization. Resolve card ownership through the existing exact/profile ladder, retaining ambient routing only for the single-backend case.
* fix(desktop): resolve plugin roots and gate the Basic layout
The review found that the first plugin build was seeded with a different installation’s fixed path, and the director ruled that flag-off layouts must match main. Resolve the running desktop’s plugin root before seeding a plugin build and register Basic only when onboarding is enabled. Keep the runbook wording and the ordinary four layout presets intact.
* fix(desktop): clear review-fix slop findings
The slop gate flagged an undocumented layout-data assertion and unknown-return types in the new test selectors. Record the layout registry invariant and preserve each selector’s return type. The only remaining production finding is the accepted connector-tools baseline.
* fix(desktop): detect the OS language on a fresh install
The review found that the merged English config default prevented the
desktop from probing the OS language on a fresh install. Add an opt-in
saved-values read so an absent choice remains distinct from saved English.
Preserve default-valued English only for explicit language saves; unrelated
settings saves must not turn a merged default into a language choice.
Older backends ignore the new query options and keep returning merged
English, preserving their existing desktop behavior.
* test(desktop): make the flag-off layout registry test deterministic
The flag-off test awaited the full controller import, pulling in the UI
graph and installing application watchers just to read layout presets.
That import took 9.5 seconds locally and timed out in the director's run.
Move the existing trees and registration into a small layout-presets
module. Production and the synchronous test use the same flag-gated
registration, without starting the controller in the test. Keep the real
registry invariant and dispose the test's contributions after completion.
* fix(desktop): keep the transcript parser and ::ask behind the onboarding flag
Register the guided chat's question card only with onboarding enabled.
Restore main's whole-paragraph parser and contribution rendering when the
flag is off, including its streaming prose behavior. Keep segmentation for
the guided flow until B4 decides the parser's wider use.
Restore main's two parser test files so its existing product and plugin
contracts remain the flag-off check.
* test: drop the onboarding and connector tests pending a later ticket
Apply the director's ruling to remove B1's added test files and restore
main's existing suites. Keep only the gateway route-reader mock contract
that main's profile tests need against the shipped activation behavior;
their cases and assertions stay intact.
The flow's shape is not settled and B3/B4 rewrite it. The connector layer
will also be reworked. The live CDP run is the flow check until a follow-up
ticket brings tests back.
---------
Co-authored-by: brooklyn! <brooklyn.bb.nicholson@gmail.com>
Since 338bf9ea9a _check_via_local_git reads _github_branch_tip directly; patching _upstream_main_sha intercepted nothing, so on the CI runner the check made a live GitHub call and behind stayed None. From #107835 (banner half; the anon_auth half landed via #107822).
Polish after #107993: `clearAllTranscriptTails` sat next to the cache's
`clearTranscriptTails` differing by one word that did not encode which
store each empties; rename it `clearTranscriptTailPaging` and keep the
WHY at the one call site instead of repeating it in the JSDoc. The
gateway-switch test resets paging state in afterEach through the helper
(covers the LRU order too) rather than an inline atom reset that a
failing assertion would skip. Drop a duplicated "capture before await"
sentence in getLatestSessionMessages.
Under gateway.multiplex_profiles, os.environ holds the DEFAULT profile's .env. Several
adapter-owned authorization gates still read GATEWAY_ALLOW_ALL_USERS, GATEWAY_ALLOWED_USERS
or their platform allowlist/allow-all raw from os.environ, so the default profile opting
into open access opened every secondary email/QQ/WhatsApp/Matrix/Teams/Slack/LINE/DingTalk
bot to any sender (email additionally skipped From: authentication), the default's Matrix
allowlist decided who may approve tool calls on a secondary bot, and a secondary that
opted in only in its own .env was silently deny-all.
Every such read now goes through the adapter's existing module-local scoped reader
(gateway.platforms._shared.get_scoped_secret / matrix _startup_env_secret): profile
scope first, scoped miss = default, never os.environ; the unscoped default-profile and
single-profile paths keep the environ read, where it IS the profile's own value.
Sites: email _allow_all_senders/_allowlist_in_effect; qqbot _open_dm_opted_in;
whatsapp_common _open_dm_opted_in/_live_dm_allow_from; teams _card_action_denied;
matrix _is_authorized_user, MATRIX_ALLOWED_USERS, MATRIX_IGNORE_USER_PATTERNS,
_extra_csv_set (allowed/free-response rooms); slack _slack_allow_bots/_slack_api_human_users;
line _truthy_env/allowlist (allow-all, user/group/room allowlists); dingtalk _extra_get
(allowed_users/chats, free-response chats, require_mention).
Live repro (temp HERMES_HOME, multiplex on, default env GATEWAY_ALLOW_ALL_USERS=true,
secondary scope without opt-in): EmailAdapter._allow_all_senders() True -> False,
QQAdapter._open_dm_opted_in() True -> False, Matrix _is_authorized_user('@stranger')
True -> False, Teams card action allowed -> denied.
Co-authored-by: Drexuxux <drexux0@gmail.com>
Co-authored-by: MoonsvnLyn <FirmamentalSpring@users.noreply.github.com>
Co-authored-by: svector-anu <anuoluwakolapo94@gmail.com>
Co-authored-by: babatorik <durgun.ismail@gmail.com>
Co-authored-by: salch-cred <salch-cred@users.noreply.github.com>
Fleet users read "Hermes backend for profile X exited (1)" as a crash and
raise Warm Bot Backends past their profile count to make it stop. Name the
setting, the defaults, what the exit line means, and which operations take
a slot so the knob is tuned for active bots instead of total profiles.
A bare `astra: 0.85` in compression.model_thresholds was written for the Codex
OAuth route, where Astra is capped at 272K and 50% would compact at ~136K. The
key is substring-matched on the model name alone, so it also fired on
openai/gpt-6-astra via OpenRouter and Nous, where the window is 1.1M: the user's
0.5 global threshold was silently replaced by 0.85 and the session sat at 620K
(~59%) without compacting.
Keys may now carry a provider prefix: `"openai-codex:astra": 0.85` applies only
when the session's provider is openai-codex; bare keys keep their route-agnostic
behaviour. Ranking is by model-substring length with scope as the tie-break, so
`astra-900k` still outranks `openai-codex:astra` for the 900K picker. The
provider flows through ContextCompressor (ctor + update_model), the ContextEngine
base class and the TUI hot-reload path, so a /model switch between routes
re-scopes the override.
- _git_run(network=True) now carries a 300s timeout; a dead-stalled fetch
(HTTP/2 to GitHub on some networks, black-holed proxy) becomes a failed run
whose stderr names the stall instead of an update pinned on
'Fetching updates...' forever (#93759, #95777). Local git stays unbounded.
- The apply path prunes stale .git/shallow grafts alongside its existing
lock/tmp_pack cleanup, so installs that already accumulated grafts from
past depth-1 checks (#105951: 57 entries) heal on their next update, not
only on --check.
Every 'git fetch --depth 1' in 'hermes update --check' (and the past
banner passive checks, before #107648 moved them to the GitHub API)
appends the fetched tip to .git/shallow as a new graft and git never
removes the previous one, so a long-lived shallow installer checkout
accumulates one graft per check (57 observed). The stale grafts break
merge-base and push 'hermes update' into the orphan-divergence reset
path with a rescue ref on every run.
prune_stale_shallow_grafts() now runs after each successful depth-1
fetch in 'hermes update --check' and clears the grafts already
accumulated by past checks: it keeps only the boundaries still
protecting referenced tips (HEAD, FETCH_HEAD, every ref tip) and
atomically rewrites .git/shallow, restoring the original file if the
trimmed set breaks history walking. The dropped commits are already
unreachable; their objects are left for git gc.
Rebased onto main after #107648: the banner.py hook is dropped (the
passive check no longer git-fetches); the update --check prune and the
cleanup of already-accumulated grafts are kept.
(cherry picked from commit 6174837fc5b9f4cc3d4d46dc1b2d9a2f6b83c120)
A GitHub outage, a rate limit, a corporate proxy intercepting TLS and a DNS
failure all rendered as the same generic line, so #105855 read as a Hermes
bug during a run of GitHub incidents. The main process now classifies the
failure (HTTP status incl. 403/429 rate-limit and 5xx outage wording, DNS,
timeout, connection refused/reset, TLS) into one actionable sentence; the
overlay shows it under the title and About appends it to the status line.
* feat(wisdom): add trusted publish and install foundation
* feat(wisdom): add private contribution loop
* feat(wisdom): add managed consumption workflows
* fix(wisdom): close cross-repository safety gaps
* fix(wisdom): align local package and lifecycle policy
* fix(wisdom): require explicit profile setup
* docs(wisdom): repin reconciled gateway head
* fix(wisdom): fence content downloads and approval receipts
* docs(wisdom): record generation-fenced downloads
* docs(wisdom): record unified delivery PR
* fix(ci): stop passing invalid classifier inputs
* docs(wisdom): remove internal requirements ledger
* feat(wisdom): localize dashboard and desktop copy
* feat(wisdom): complete local contribution and consumption UX
* style(wisdom): satisfy desktop lint
* chore(wisdom): refresh requirements pin
* test(dashboard): allow formatted profile copy
* test(wisdom): stabilize desktop interaction coverage
* fix(wisdom): surface dashboard action failures
* fix(wisdom): add repeatable Portal demo login
* feat(wisdom): add actionable skill notifications
* feat(wisdom): add notification install and update actions
* fix(wisdom): make Telegram skill alerts actionable
* fix(wisdom): always refresh demo Agent login
* feat(wisdom): embed Telegram notification actions
* fix(wisdom): preserve Telegram notifications after actions
* fix(wisdom): keep Telegram notification cards readable
* feat(wisdom): add Telegram candidate approval flow
* feat(wisdom): explain Telegram qualification reasons
* fix(wisdom): reconcile cross-surface candidate actions
* feat(telegram): add Collective Wisdom management command
* chore(wisdom): refresh Gateway contract pin
* chore(wisdom): advance Gateway contract pin
* feat(wisdom): align command UX across clients
* feat(slack): add Collective Wisdom management parity
* feat(wisdom): add security and professionalism reviews
* feat(wisdom): add first-time qualification guidance
* feat(wisdom): simplify qualification sharing choices
* feat(skills): add optional editorial metadata
* feat(wisdom): enrich legacy skill presentation
* fix(wisdom): harden review and update boundaries
* fix(wisdom): emit canonical review timestamps
* fix(wisdom): align with merged gateway and main
* wisdom: add agent-led sharing core (policy, evidence, schemas, templates, delivery, weekly job, share/install flows)
- hermes_wisdom/agent_led/: policy resolution (server > local > defaults),
7-day evidence builder that excludes bundled/hub/managed skills and
dismissed/handled/recently-suggested content hashes, strict pydantic
schemas for agent output with repair-or-reject, fixed copy templates
(Share / Teammate / Published / Update / Mute), idempotent retried
delivery ledger with stale-action resolution, weekly review job,
resumable Share and Install flows.
- prompts/: candidate review, recipient recommendation, share packaging.
- tests/wisdom/test_agent_led.py: 30 tests.
* wisdom: agent-led renderers and button action dispatcher
- render.py: Telegram HTML, Slack blocks, Desktop payload; editorial name
is the emphasized line, product label stays separate.
- actions.py: resolve opaque wa:<action>:<dedup> targets via the delivery
ledger; Not now -> dismissal, Mute -> fixed options, Share -> resumable
packaging flow, Install/Update -> plan command. Never publishes/installs.
* wisdom: CLI verbs, agent_led config default, conversational catalog skill
- hermes wisdom browse/review-week/act/share/dismiss/mute (all --json).
- wisdom.agent_led config block, default enabled.
- SKILL.md rewritten so natural-language catalog questions map to the CLI
verbs, share/install flows and fixed notification templates.
* wisdom: wire agent-led weekly review into gateway tick and Telegram buttons
- gateway housekeeping tick calls maybe_run_weekly_review with a home
channel sender when a Telegram adapter is available.
- Telegram: wa: callbacks resolved through the ledger (stale-safe), mute
duration keyboard, send_wisdom_agent_recommendation rich card + fallback.
* fix(wisdom): integrate local mediation and harden model and setup boundaries
* fix(wisdom): honor authoritative recommendation policy and defer on failure
* fix(wisdom): synchronize opaque suppression and recheck delivery preferences
* feat(wisdom): route weekly selection through the session-owned assessment queue
* fix(wisdom): prepare and submit the reviewed generated share package
* feat(wisdom): separate native Share preparation from publication consent
* feat(wisdom): sync native mute choices through a leased preference outbox
* feat(wisdom): bind native mute controls to durable preference choices
* feat(wisdom): add scoped desktop and dashboard notification settings
* fix(wisdom): revalidate feed recommendations before assessment and delivery
* fix(wisdom): persist validated delivery receipts before completing notices
* feat(wisdom): add private notification claim and receipt client
* Persist Wisdom send reservations and recover delivery acknowledgements
* Route legacy Wisdom controls through current native review
* Add typed private Wisdom operation outcome client
* fix(wisdom): make agent-led advice usable in the local demo
* fix(wisdom): keep requested consent outside proactive limits
* fix(wisdom): distinguish unavailable assessments and preserve digest text
* fix(wisdom): assess ongoing usefulness beyond the current task
* fix(wisdom): restore immediate qualification sharing controls
* fix(wisdom): separate qualification review from installation advice
* fix(wisdom): collapse review checklists and simplify sharing copy
* fix(wisdom): show compact sharing progress and publication receipts
* fix(wisdom): require credential prefixes rather than matching skill names
* fix(wisdom): finish package checks before presenting sharing consent
* fix(wisdom): scan local skills before qualification cards
* fix(wisdom): update moderation results on existing sharing cards
* fix(wisdom): keep sharing review accessible from receipt cards
* fix(wisdom): align mediated review cards and collapsible checks
* fix(wisdom): clarify clean security summary wording
* fix(wisdom): normalize consent plans and add explicit recheck
* fix(wisdom): keep install and update receipts concise
* fix(wisdom): collapse assessments and deduplicate operation cards
* fix(wisdom): restore private Portal review from native cards
* fix(wisdom): sync Portal publication to original consent card
* fix(wisdom): show local skill version on sharing cards
* fix(wisdom): skip agent recommendations for self-published versions
* fix(wisdom): simplify candidate notices and local-edit recovery copy
* feat(wisdom): submit locally reviewed packages with one confirmation
* feat(wisdom): expose safe receipt and outcome sync recovery
* wisdom: onboarding notice says detect and share, names the user's own skill
Copy review from the product owner on the first and returning
qualification notices (fixed delivery mode):
- the feature blurb now says the org enabled detection *and sharing*
- both notices say the detected skill is one the user created
- both close with an exclamation mark
Applied identically to hermes_wisdom.notice, the desktop and web i18n
strings, and the tests that assert the sentences.
* wisdom: one opener, no approval line, ask to share after the skill is shown
Product owner review of the candidate card.
- The Hermes written card now opens with the same sentence as the fixed card
("Your organisation has enabled Collective Wisdom, a feature designed to
automatically detect and share useful skills across all team members.")
instead of its own blurb, so there is one first time message.
- "Nothing is shared without your approval." removed from Telegram, Slack
and Desktop. The buttons already make the permission explicit.
- "Would you like to share?" no longer appears before the skill is named.
It is now the last line, after the skill name, description, why suggested
and the checks, and reads "Would you like to share it?" (matching the
agent led template wording).
Tests updated for the new order; proposalNotice removed from all desktop locales.
* wisdom: American spelling, organization
Product owner decision: user facing copy uses American spelling.
Changes "Your organisation" to "Your organization" in the chat notice,
the Hermes written card opener, the desktop and web strings, and the
tests that assert them. Identifiers such as nas_organisation:* and the
German and French locales are untouched.
* wisdom: candidate card copy round 4 (owner review)
Apply the product owner's round 4 copy decisions to the Hermes Collective
Wisdom candidate card on Telegram, Slack, Desktop and the shared views:
1. Hermes-written cards are titled "Hermes Collective Wisdom" instead of
the bare "Collective Wisdom".
2. The "Reusable skill ready to review" line is gone from the candidate
card (Telegram rich card and plain fallback, legacy agent-led share
template).
3. The skill name and description are labelled: "Skill name: <name>" and
"What it does: <description>" (Telegram, Slack, Desktop).
4. "Why suggested:" is now "Why others might benefit:".
5. A passing professionalism review reads "Safe to share at work ✓ (no
inappropriate content found)" with no per-check bullets and no "Pass";
a failed review reads "Needs a look before sharing at work (possible
inappropriate content)" and lists only the checks that flagged
something. Pending/unavailable wording is unchanged.
6. Telegram button toasts: "Will ask later...", "Preparing more
details...", "Sharing...".
7. Qualification reasons: "You used this skill consistently across many
days." and "You've really refined this skill."
8. prompts/wisdom_candidate_review.md asks for a compelling
editorial_name, a simple one_line_description and a compelling
why_coworkers_benefit under 300 characters; "Be concise and
convincing." becomes "Be concise and compelling: the goal is that the
user wants to share it."
Tests updated for the new strings; review_text() gains direct coverage.
* wisdom: re-apply owner copy after rebase
- Native share cards (advice_view/interaction_view): drop the approval line, ask "Would you like to share it?" as the last line after the checks
- Hermes-written completion card titled "Hermes Collective Wisdom"
- Qualification reasons use the owner wording (consistently across many days / really refined)
- American spelling (organization) in remaining English copy
- Desktop test asserts the current Share button; web test matches the returning notice
* fix(wisdom): pin reconciled Gateway and verify Unicode hash vectors
Pin Gateway 60cd2d6b613ae3cd4a6e65155d1142006d907e78 and byte-identical producer artifacts. Verify every content-order case and package-manifest binding. Validation: 186 focused Python tests, Ruff and contract verifier.
* fix(wisdom): reconcile optional SDK tests and frontend lint
* fix(wisdom): default to agent-written notification summaries
* fix(wisdom): restore deferred install review and browse controls
* feat(wisdom): inspect installed setup with exact package provenance
* feat(wisdom): run native-approved installed setup steps with durable evidence
* fix(wisdom): recover interrupted setup with explicit native consent
* feat(wisdom): hand native installs into guided setup review
* fix(wisdom): continue requested setup with fixed notification copy
* fix(wisdom): preserve setup while waiting for a session model
* fix(wisdom): expose canonical setup review controls on desktop
* fix(wisdom): resume setup after recorded automatic updates
* fix(wisdom): make missing setup prerequisites recheckable
* chore(wisdom): align Agent with verified Gateway contract
* fix(wisdom): stop guessing team slugs in portal links
* fix(wisdom): retire pending advice on account sign-out
* fix(wisdom): cancel advice after terminal account revocation
* fix(wisdom): fence feed responses across account sign-out
* fix(wisdom): checkpoint signed-out feed before reactivation
* fix(wisdom): link proactive advice to scoped notification settings
* fix(wisdom): coalesce queued publication recommendations by version
* fix(wisdom): keep package review navigation local and deferable
* fix(wisdom): reflect installed state in discovery controls
* fix(wisdom): show exact checks before command confirmation
* chore(wisdom): pin bounded analytics privacy contract
* chore(wisdom): pin retired legacy notification contract
* feat(wisdom): review publisher usage with exact sharing copy
* fix(wisdom): align discovery and review check summaries
* fix(wisdom): show expired consent before confirmation
* fix(wisdom): require fresh review for legacy install controls
* fix(wisdom): preserve review expiry across check toggles
* fix(wisdom): retain update policy in native install reviews
* fix(wisdom): surface failed native card edits
* fix(wisdom): persist local command approval reviews
* fix(wisdom): use saved approvals for messaging commands
* test(wisdom): provide scan result in setup handoff fixture
* test(wisdom): exercise Telegram approvals with saved review state
* fix(wisdom): retain suppression policy for offline deferral
* fix(wisdom): reconsider candidates after deferred suppression expires
* fix(wisdom): bind review checks and report verified readiness separately
* fix(wisdom): persist accepted publication intent and recover exact outcomes
* fix(sync): pin UTF-8 tree ordering across writers
* chore(wisdom): pin organisation-scoped Gateway authorization
* fix(wisdom): restrict consent delivery to user-facing sessions
* chore(wisdom): refresh reviewed Gateway contract pin
* fix(wisdom): preserve kept tools in Blank Slate exclusions
* test(auth): reset anonymous fixture with a profile-scoped cache
* fix(wisdom): gate local surfaces and work on current profile entitlement
* fix(wisdom): invalidate quiet tool cache on entitlement changes
* test(wisdom): authorize local consent gateway fixtures
* fix(wisdom): keep entitlement decoding free of native crypto imports
* test(wisdom): provide local entitlement to demo CLI subprocess
* ci: leave upstream workflow unchanged in Wisdom PR
* fix(wisdom): ship package and contracts in Nix wheels
---------
Co-authored-by: hbizi <36184542+hbizi@users.noreply.github.com>
bedrock.guardrail was only attached on the Converse route (guardrailConfig in the
body). Claude on Bedrock goes through the AnthropicBedrock SDK, i.e. InvokeModel,
whose body has no guardrailConfig, so the default Claude route ran with no guardrail
at all (#52179; live-verified by JiaDe-Wu: the blocked word came back through Hermes).
Bedrock reads the guardrail for InvokeModel from X-Amzn-Bedrock-GuardrailIdentifier /
-GuardrailVersion / -Trace headers. Attach them as default_headers in
build_anthropic_bedrock_client so every AnthropicBedrock client Hermes builds
(primary init, /model switch, fallback, per-request rebuild, auxiliary) enforces the
same guardrail, with prompt caching / thinking / 1M context kept (the reason Claude is
not routed through Converse).
InvokeModel blocks do NOT change stop_reason (stays end_turn) and return the guardrail's
canned text as an ordinary assistant reply, flagged only by
amazon-bedrock-guardrailAction=INTERVENED in the body (SDK: response.model_extra).
AnthropicTransport.response_finish_reason maps that to content_filter so the loop runs
its refusal handling instead of reasoning over the canned text; _derive_finish_reason
uses it for the anthropic_messages branch.
Mantle (openai.gpt-5.x) is documented by AWS as not supporting Guardrails on the
Responses endpoint; the docs now say so instead of promising "all model invocations".
Header mechanism proposed in #52312 by @JoaoMarcos44 (stale base, 7-file conflict,
detection keyed on a Converse-only stopReason); reimplemented on current main.
Live probe (local sink, SigV4 fake creds): before, no X-Amzn-Bedrock-* header on the
InvokeModel request; after, headers present, SigV4 intact, INTERVENED → content_filter.
wipeSessionListsForGatewaySwitch cleared the persisted transcript-tail
cache but never the in-memory $transcriptTailBySessionId atom. Before this
stack both an ambient local read and an ambient remote read of a recycled
stored id landed under the bare-id key, so the newer backend simply
overwrote the older. Owner-keyed entries coexist instead, and the
unique-match lookup that gates "Show earlier" sees two candidates and
fails closed for that session until eviction. Wipe the atom alongside the
cache.
A custom HERMES_HOME (outside the profiles tree) reported `profile: null`,
so the tail entry landed under profile '' while the session's owner hint —
built from list rows that _serving_profile() stamps "default" — looked up
'default'. Two identities for one backend; the exact-key lookup missed and
"Show earlier" stayed hidden for the Desktop's own sandboxed and
HERMES_HOME-overridden installs (electron/main.ts resolveHermesHome).
The messages endpoint now returns the same _serving_profile() stamp the
list rows carry, so the Desktop keys a page under the owner it already
routes the session by; the inline resolver and its function-body import go
away. On the renderer, the ambient profile used for backends that predate
the field is captured at request time alongside the connection, so a
profile switch mid-read cannot re-key the page.
TranscriptTailState.profile was documented as the owning profile but now
holds the request ROUTE (the owner is the map key). Rename the
recordTranscriptTail parameters to route/owner, fix the field doc, say why
a bare-string scope still defaults to 'default' while an object scope does
not, and correct transcript-tail-cache's claim that it mirrors the
in-memory key (it keys by request scope and the two stores never cross-read).
A backend that predates the `profile` response field left the owner key's
profile empty, while the resume path records the same session under
{connectionId, profile: 'default'}. Two entries per backend meant the
owner-hint lookup only ever saw the resume entry and the no-hint fallback
returned nothing — "Show earlier" stayed hidden for exactly the mixed
Desktop/backend versions the PR body says remain readable.
An absent field now falls back to the ambient request profile ('default'
when none is active): that IS the profile an untagged read lands on. A
`null` field (custom HERMES_HOME) still stays distinct from 'default'.
api/sessions.ts imported $connection from @/store/session to learn whether
an untagged read lands on the local pool. That closes a real module cycle
(api/sessions -> store/session -> session-unread-remote -> @/hermes ->
api/sessions), inert only because the read sat inside a function body, and
it breaks the "no store import" contract client.ts documents for exactly
this reason.
Publish the fact through the seam that already exists: store/session's
setConnection pushes local/remote into client.ts (setApiRequestLocalMode,
next to setApiRequestProfile / setApiRequestConnection), and
ambientOwnerConnectionId() answers "which backend serves an untagged
request" for the owner key. A null descriptor (reconnect blip) keeps the
last mode, matching rescopeConnectionScopedStores.
Pairing tests stub only the Electron preload bridge so the real jsdom
window and its event APIs survive; paging fixtures use nonzero
deterministic timestamps so the conversion-time Date.now() ID fallback
cannot produce a mismatch across separate conversions.
(Second commit of #107839, minus the test_anon_auth_core.py change that
main already carries as 0c0983ab21.)
The SDK-private _build_headers probe was spelled three times across two files; the api-key shard also re-asserted the bearer invariant the portal test owns. dict(headers or {}) → dict(headers): every caller passes _beta_header's dict.