Commit Graph

44590 Commits

Author SHA1 Message Date
madpower2000
cb5fcb0cd5 catalog: bump telegram-notify to v1.1.2
Bumps the pinned sha to the v1.1.2 release and version to 1.1.2.
v1.1.2 fixes the codex-telegram-notify attribution/link and suppresses
duplicate lifecycle notifications for Telegram-origin sessions, while
preserving existing subagent/smart-approval filtering.
2026-09-26 17:59:01 -07:00
Chris Mish
ca01cd4ef7 catalog: update pixel-worlds to v0.3.0 2026-09-26 17:58:36 -07:00
Austin Pickett
39aaa5c326 fix(desktop): expose APPROVAL_RESPOND_TIMEOUT_MS via the plugin SDK
Plugins may only import @hermes/plugin-sdk (no-restricted-imports); the
hermes-bots group-approval path pulled the constant from @hermes/shared and
failed check:lint. Re-export it from the SDK and import it there; the
plugin-sdk test mock gains the export.
2026-09-26 20:58:31 -04:00
Austin Pickett
aff4034f91 fix(desktop): ride the backend approval window for approval.respond
The client rejected its own approval.respond RPC after the generic request
timeout (120s shared default, 30s desktop) while the backend still waits the
full approvals.timeout (300s) for the user: answering later surfaced a false
"request timed out" over an approval the backend then applied anyway.

approval.respond now carries APPROVAL_RESPOND_TIMEOUT_MS (300s, the backend
default); ambientRequestFor forwards the optional deadline so session-routed
approval RPCs can raise their timeout; the hermes-bots group-approval path
rides the same deadline.

Fixes #60654
2026-09-26 20:58:31 -04:00
Austin Pickett
0cd93f0268 fix(mcp): kill Windows stdio MCP orphan trees (#61059)
Windows has no POSIX parent-death supervisor/killpg safety net, so an
ungraceful exit of the hermes process left every stdio MCP child tree
(npx.cmd -> node.exe) running as orphans with ParentId=null, piling up
across session restarts.

- _run_stdio now attaches the process to a KILL_ON_JOB_CLOSE job object
  before spawning stdio children (self-guarded no-op off Windows), so the
  whole child tree dies with the parent at the kernel level.
- Windows reaps kill the process tree (direct child + descendants) in the
  lifecycle orphan sweep and the spawn-ledger startup sweep, where there
  is no pgid to group-kill.

Fixes #61059
2026-09-26 20:58:16 -04:00
kshitijk4poor
7e3481ab6d chore(plugin-catalog): pin claude-subscription-directsdk to ef73726 2026-09-26 17:58:12 -07:00
teknium1
cc9d1cfc81 chore(cortexlayer): catalog review edits, map contributor email(s) (#124341) 2026-09-26 17:57:47 -07:00
Miguel Cota
cd5d751458 feat(plugin-catalog): add cortexlayer memory-provider entry
Standalone MemoryProvider plugin for CortexLayer (github.com/Cortex-Layer/cortexlayer-hermes-plugin),
a linked-page long-term memory service with link-expansion retrieval. Platform mode only
(hosted api.cortexlayer.net, API-key authenticated) — implements the required lifecycle, all
5 memory tools (memory_add/retrieve/update/delete/relink), and the on_session_end /
on_pre_compress hooks (checkpoint API v2).

Owner submission (I maintain the plugin repo). Manually verified end-to-end against a real
Hermes instance before this submission (multi-turn conversation, confirmed sync_turn/prefetch
working with the profile's built-in memory disabled to remove ambiguity).
2026-09-26 17:57:47 -07:00
teknium1
7f3722a587 chore(sigrex): catalog review edits, map contributor email(s) (#124336) 2026-09-26 17:57:23 -07:00
Zsolt F
19d87b82ef feat(plugin-catalog): bump sigrex to 1.0.1 (add banner) 2026-09-26 17:57:23 -07:00
James
1022be3cf3 plugin-catalog: bump thomas to v0.1.1 2026-09-26 17:56:57 -07:00
Austin Pickett
3e68e6ba1a test(desktop): advance fake timers by BACKEND_BOOT_WAIT_TIMEOUT_MS, not a hardcoded 45s
The boot-budget tests hardcoded the old 45s (and a 60s cushion) budget, so
raising BACKEND_BOOT_WAIT_TIMEOUT_MS to 180s made them time out or assert
before the deadline elapsed. Drive them from the constant so the next
budget change moves both together.
2026-09-26 20:54:47 -04:00
Austin Pickett
895ef9b839 fix(desktop): raise backend readiness deadline 45s to 180s
A cold backend boot (plugin discovery + route mounting at web_server
import time) takes 45-60s on slower hardware, so the 45s readiness
deadline made first-boot readiness a coin flip. Every lost race tore
down a healthy-but-slow backend and re-drove boot, cascading into
minutes of 'not connected' and orphaned python processes.

Raise both ends of the paired budget in lockstep: the main-process
readiness poll (DEFAULT_BACKEND_READY_TIMEOUT_MS) and the renderer
cold-boot wait that mirrors it (BACKEND_BOOT_WAIT_TIMEOUT_MS). The
poll returns the moment the backend responds, so fast machines see
no change.

Fixes #63454

Based on #63456 by @frohsinnllc

Co-authored-by: frohsinnllc <frohsinnllc@users.noreply.github.com>
2026-09-26 20:54:47 -04:00
Austin Pickett
84f87a18c0 fix(desktop): scope the session-cookie mirror by partition and gate the 401 replay
Review follow-ups on #123008 (andrexibiza):

- P1: key the in-memory mirror by the resolved OAuth partition (the same
  owner resolveOauthPartition picks for the jar) + origin, so two
  same-origin sub-path gateways on separate jars can never see each
  other's credentials through the explicit Cookie header. Cookies keep
  their effective Path (RFC 6265 path-match on attach) and an expiring
  Set-Cookie (Max-Age<=0 / past Expires / empty value) deletes the entry.
  Tests: A→B→A stays separate, a B-only 401 clears only B, path scope,
  deletion.
- P2: the silent re-login + single retry now runs only when
  shouldReplayAfterCookie401 holds — the 401 body is the dashboard auth
  gate's structured pre-handler refusal ({error: unauthenticated |
  session_expired, reason}) AND the operation is idempotent (GET/HEAD) or
  vouched replay-safe by the caller (replayOn401, set by the WS-ticket
  mint). Application-level 401s and arbitrary mutations keep the
  no-replay rule of requestWithOauthFallback.
- lint: perfectionist/sort-imports for the new module import.
2026-09-26 20:47:10 -04:00
austinpickett
adc3678150 fix(desktop): mirror remote session cookies in memory and retry once on 401
The persist:hermes-remote-oauth partition family drops its hermes_session*
cookies in the field (Windows %3A profile folders, lazy hydration, flush
races), and electronNet's useSessionCookies intermittently omits the cookie
entirely → every authed REST call and WS-ticket mint 401s as no_cookie right
after a successful sign-in (#61457).

- New remote-session-cookies.ts: process-lifetime per-origin Set-Cookie
  mirror (memory only, never persisted).
- The login window snapshots the fresh jar into the mirror on success; every
  authed REST response records its Set-Cookie headers.
- fetchJsonViaOauthSession attaches the mirror as an explicit Cookie header
  (an explicit header bypasses the network stack's jar lookup) unless the
  caller supplied its own Cookie.
- On a 401 the mirror is dropped for the origin, one silent re-login is
  forced, and the request is retried once (safe: a 401 means the server
  rejected the request before executing it).

Fixes #61457
2026-09-26 20:47:10 -04:00
Austin Pickett
fc9349082f fix(desktop): type scanWindowsProcesses rows as strings so tsc accepts both call sites 2026-09-26 20:42:11 -04:00
Austin Pickett
3aa0ac5e0d fix(desktop): kill external autostart venv holders before update hand-off
The Windows update/uninstall hand-off aborted with 'venv shim still
locked' whenever an autostart Hermes process held the venv: the gateway
Startup item and the dashboard Scheduled Task are launched outside the
desktop app, so releaseBackendLock() never saw them and every hand-off
failed after the 15s gate.

- new isExternalVenvHolder() selector in venv-holder-select.ts: exe must
  live under <venv>\Scripts\ AND be unambiguously a Hermes program
  (hermes.exe shim, python -m hermes_cli, python -m hermes) — far
  narrower than the install-root substring matching that sank #62445,
  so unrelated processes mentioning the install root or borrowing the
  venv interpreter are never tree-killed
- releaseBackendLock() kills those holders before the release gate and
  re-scans inside every gate pass, so a respawning autostart holder
  loses the race; the shim-lock probe remains the backstop abort for
  non-Hermes holders
- the Win32_Process scan is shared with the hindsight-daemon sweep

Based on #62445 by @LeonSGP43 (approach credited; matching narrowed).

Fixes #62311
2026-09-26 20:42:11 -04:00
soyelmismo
ad4c40d8ec catalog: pin antigravity-subscription-directsdk v1.0.4 2026-09-26 17:41:41 -07:00
teknium1
1961a158bf chore(catalog): disclosure for antigravity-subscription-directsdk.yaml (merge-time review edits) 2026-09-26 17:41:41 -07:00
soyelmismo
05cfa2cf26 catalog: bump antigravity-subscription-directsdk to v1.0.3 2026-09-26 17:41:41 -07:00
seref
5939ce460f fix(plugin-catalog): pin Index Network at the admission fix
Move the entry to 0.47.4, where the desktop half is opt-in and the declared env matches the plugin.
2026-09-26 17:41:28 -07:00
seref
b7e5b67557 feat(plugin-catalog): add the Index Network plugin
Pin indexnetwork/hermes-plugin at the 0.47.2 commit so Hermes can install it by catalog name.
2026-09-26 17:41:28 -07:00
Apostol Apostolov
77e3e5cc15 fix(plugin-catalog): repin SDK-only session pin interim 2026-09-26 17:41:17 -07:00
Apostol Apostolov
b0f569b09f feat(plugin-catalog): update drag-to-pin-session to SDK-backed pin 2026-09-26 17:41:17 -07:00
Brooklyn Nicholson
9bb2ea1b5c fix(desktop): make the Floating Composer setting findable as a dock lock
Settings search matches lock, peel and pop out, and the description says what off means now that off is the default.

Co-authored-by: networthexplained <300128320+networthexplained@users.noreply.github.com>
2026-09-26 19:00:41 -05:00
Brooklyn Nicholson
4b1da97299 fix(desktop): require a deliberate drag to peel the composer out
Raise the docked peel threshold from 16px to 48px so a brush of the grab ring no longer undocks it.

Co-authored-by: networthexplained <300128320+networthexplained@users.noreply.github.com>
2026-09-26 19:00:41 -05:00
Brooklyn Nicholson
e928574ab3 fix(desktop): lock the docked composer to the dock by default
Drag-to-float gestures now default off on a fresh install. A composer that was already floating before the flip keeps its gestures, and a stored choice wins either way.

Co-authored-by: networthexplained <300128320+networthexplained@users.noreply.github.com>
2026-09-26 19:00:41 -05:00
Brooklyn Nicholson
802ae8544b test(docker): a mounted host dir follows the fallback mount 2026-09-26 18:27:10 -05:00
brooklyn!
b686f1b40b fix(docker): do not invent a workspace mount for a raw Windows override
The mount flag is off in that case. The override must fall back to the
sanitized config cwd, not docker run -w /workspace.
2026-09-26 18:27:10 -05:00
brooklyn!
37933ff662 fix(docker): ignore a non-string host root when translating a mounted path
A MagicMock env attribute is not a workspace. Treating it as one crashed
the Windows search path on startswith.
2026-09-26 18:27:10 -05:00
brooklyn!
1a72042115 fix(docker): bind a Windows workspace when /workspace is already claimed
A volume that already owns /workspace skipped the configured working
directory, so tools treated that host path as unmounted. Bind it at a
second mount, or point tools at the volume that already has it, for any
drive path.
2026-09-26 18:27:10 -05:00
Brooklyn Nicholson
50873d2154 fix: picker/input cluster — vendor casing, variant tags, submenu keyboard path, stale moa pick, CJK search star
Six fixes for the wave-7 picker/input cluster:

DeepSeek -> "Deepseek") and left the gemini- branch's words lowercase
("Gemini 2.5 pro"). Vendor casing + parameter counts now applied after
title-case (GLM, DeepSeek, MiniMax, OpenAI, ERNIE, MiMo, BGE, VL, IT,
FP8, AI; 8b -> 8B, a3b -> A3B), and the gemini branch title-cases like
every other branch.

and was unreachable by keyboard (rows are highlighted, never DOM-focused,
so Radix's own ArrowRight never fires). The chevron is now visible on
every model row and ArrowRight (caret parked at query end) hands focus
to the highlighted trigger and opens its sub; ArrowLeft returns focus
to the search field. Consolidates #86968 + #104532.

-fast/-thinking/-preview ids to the base label. The tag now rides the
display name on every surface, and formatModelPillLabel no longer
doubles Fast for a -fast variant id.

all MoA presets were disabled: manual picks are sticky by design
(d595e636c8), but the virtual moa provider's catalog row disappears
entirely once no preset is enabled, so that one absence is
authoritative (moaPickRemoved) and the pick reseeds from the profile
default. Narrow moa-only exception — no general catalog diff.

token (nimb -> nimb*); none of the CJK routes can honour it (bigram and
trigram routes quote tokens so the star matches literally; LIKE has no
star wildcard at all), so CJK searches returned zero results. The star
is now stripped per token on the CJK path only.

measure() effect deps (stale measurements after toggling Inbox style)
and the card estimate undershot the four-line/wrapped-title worst case
(74px), painting rows over their neighbours on cold start. Card
estimate raised to the worst-case-covering 96px and the deps fixed.
2026-09-26 18:16:44 -05:00
hermes-seaeye[bot]
13f6b46daa fmt(js): npm run fix on merge (#124529)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-26 23:12:07 +00:00
Hermes Agent
b24b8149dd fix(icons): give the dev Dock its own mac-grid png
Linux uses apple-touch-icon.png as the window icon and Nix requires it to
match the full-bleed launcher icon, so keep it full-bleed and point the
dev-only app.dock.setIcon at assets/icon-mac.png instead.
2026-09-26 18:10:54 -05:00
Hermes Agent
c8094dc399 fix(icons): put the dev Dock icon on the mac grid and enlarge the mac art
Dev runs replace the Dock icon with public/apple-touch-icon.png, which the
generator rendered full-bleed, so it drew ~24% larger than its Dock
neighbors. Render it from the mac-grid master like the icns targets.

On the 824 grid the plate matches peers, but the girl inside a white tile
with a ring read small; scale her 1.12x about the plate center for every
mac target.
2026-09-26 18:10:54 -05:00
Hermes Agent
f4795c922c fix(desktop): tell the user when a clarify request never reached the app
A pending clarify card waits on its gateway clarify.request. When that
frame is lost the card sat as a disabled preview until the 300s timeout
with no hint of what went wrong.

After a 4s grace the card now asks the owner socket for
session.events.since, which re-delivers the session's open requests, so a
request the backend still holds parks and the card goes live. If nothing
turns up, single and batch cards show an inline notice (all locales)
pointing at Stop, and drop the dead Skip/Continue actions.
2026-09-26 18:00:36 -05:00
liuhao1024
00c8e5ca48 test(desktop): cover the one-entry batch clarify shape end to end
Since the questions[]-only schema (#95907) every single question is a
one-entry batch on both the tool args and the gateway wire, yet no test
exercised that shape (called out in #98645). Lock the behavior down at
both layers:

- unit: a one-entry batch renders the batch card (not a blank/spinner
  single card) both with the wire already parked and when the request
  lands after the tool row, and answers with the qid-keyed lock
- e2e: a SINGLE_BATCH trigger drives the real chain (composer -> gateway
  -> agent -> clarify tool -> clarify.request -> renderer) through mount,
  pick, confirm, and settle for questions.length === 1

The e2e mock's trigger routing also learns to scope its has-tool-result
guard to the answering turn's own question text: the existing any-tool-
result check would false-positive once a second scripted clarify shares
the conversation history.

Adapted to main: the mock server now lives in tests-js/scripts, and a batch
confirm answers with clarify.lock.
2026-09-26 18:00:36 -05:00
Hermes Agent
2f86fae3be fix(desktop): forward --profile on the bundled-install launch too 2026-09-26 18:00:30 -05:00
Brooklyn Nicholson
fd9350a941 fix(desktop): sort the launch-profile import 2026-09-26 18:00:30 -05:00
brooklyn!
8684bf3ddc fix(desktop): forward --profile into the packaged desktop launch
Electron booted from active-profile.json and ignored argv. hermes
desktop and hermes -p <name> desktop never appended --profile, so
the packaged app kept the stored profile.

Parse both --profile spellings before startHermes, persist that
name, and append the same flag on the packaged launch. A missing
flag does not change the stored profile.
2026-09-26 18:00:30 -05:00
Hermes Agent
6d94896c78 chore: map contributor emails 2026-09-26 18:00:17 -05:00
finn763
a164569429 fix(agent): admit and sync gateway-staged attachments on remote execution backends (#110174)
Desktop paste/file attachments land in Hermes-managed staging dirs on the
GATEWAY (composer-pastes/ for large text pastes, attachments/ for dropped
files), but on the Remote SSH topology the workspace root (TERMINAL_CWD) is a
path on the SSH HOST - the two filesystems are fully disjoint, as the issue
thread confirms. Two gaps combined to reject every staged attachment with
"path is outside the allowed workspace":

- _resolve_path admitted only allowed_root + composer-paste roots, so a
  gateway-staged attachments/ path was refused outright. Admit the
  _CACHE_DIRS staging roots (attachments/, images/, cache/*, composer-pastes/)
  via a helper that asks get_cache_directory_mounts - the gateway's OWN
  payload is never a workspace escape, and the path-traversal and
  credential-deny guards in _ensure_reference_path_allowed still run after.
  Anything else outside the workspace stays blocked.
- composer-pastes/ was missing from _CACHE_DIRS, so its bytes never reached
  the remote: ssh/daytona/vercel_sandbox sync via iter_sync_files ->
  iter_cache_files, and to_agent_visible_cache_path only translates mounted
  dirs - a paste attached on a fresh session dangled on the remote host.

Tests cover the disjoint-filesystem SSH topology end-to-end (text inlines,
binary renders the synced ~/.hermes path), the still-refused stranger path,
local-backend unchanged, and the composer-pastes mount+sync enumeration.

Consolidates PR #110387 by Finn763 (the _agent_staged_path guard widening and
the SSH-topology tests, adapted to the current _ensure_reference_path_allowed
ordering) with PR #103412 by ericmaddox (whose mapping insight is subsumed by
the _CACHE_DIRS entry, which fixes both the sync and the translation).

Co-authored-by: ericmaddox <ericmaddox@users.noreply.github.com>
2026-09-26 18:00:17 -05:00
Muhammed Emin Boydak
a723194c1e fix(desktop): reserve the window-controls band on the narrow sidebar overlay (#110033)
The narrow-viewport overlay for a collapsed zone is `absolute inset-y-0` —
it starts at the viewport's top edge with its tab strip (SESSIONS | BOTS) as
the first child, so on macOS the strip slides under the traffic lights.
Docked zones already reserve that band (TreeGroup: useWindowControlsOverlap
-> paddingTop plus an absolute [-webkit-app-region:drag] spacer; top-edge
zones use usePanelTitlebar), but the overlay used neither.

Reserve it the same way: measure the native controls rect against the
overlay element and pad the strip below it, keeping the band a window-drag
target via the drag-region spacer. The overlay's data attribute now carries
the revealed pane id (it was a constant empty string), which the regression
test uses as its selector.

Salvaged from PR #110034 by Muhammed Emin Boydak (the overlap hookup, the
paddingTop + drag spacer, and the 34px-reservation test), adapted to the
current file (NO_PANE_GROUP import, per-pane data attribute).

Fixes #110033.
2026-09-26 18:00:17 -05:00
Hermes Agent
a6dadb83ba fix(desktop): strip citeturn web-citation transport markers from prose (#120587)
Model output can arrive carrying Gemini-style grounding citation markers:
private-use delimiters U+E200/U+E201 wrap a `citeturn<n>search<m>` id list
(U+E202 separates ids) - e.g. `\uE200citeturn0search11\uE202turn2search0\uE201`.
Desktop had no rule for that shape (CITATION_MARKER_RE only strips bare
numeric `[n]` markers), so the private-use code points painted as replacement
glyphs - the reported "triple bars" - and the `turn…search…` ids rendered as
literal prose, wrapping across table cells and obscuring the answer.

Add CITATION_TRANSPORT_MARKER_RE and strip it in rewriteProseSegment, the
same shielded path the numeric marker rule rides: inline code and math spans
split out first, so `$\sqrt[3]{8}$` and quoted marker text are untouched, and
the bare no-delimiter alternative only fires on the `cite` head so plain prose
and stray private-use characters (icon fonts) are left alone. A marker that
cannot be resolved to a source is dropped, never invented into a link -
matching the reporter's own expectation. Mid-stream flushes (closing U+E201
not yet arrived) are covered by the optional-tail shape.

Backend-side emission (which search provider leaks the markers into model
text) remains unisolated, as the report itself notes; the display-layer strip
is justified regardless.

Fixes #120587. No external PR existed (the catalog's linked PR #120592 is a
dead reference).
2026-09-26 18:00:17 -05:00
Phantomthedog
d38d2a8f0a fix(desktop): keep a bare URL's full text visible in chat markdown (#121007)
MarkdownLink nulled fallbackLabel whenever the link text matched the target
URL — exactly the bare-autolink case — so PrettyLink fell through to
urlSlugTitleLabel and rendered a host-only label (`ncpssd.org` for
https://www.ncpssd.org/), with the address readable only via hover/inspect.
The user could not read an address sent in chat.

The link's own text is always a legitimate fallback label; pass it through.
Labeled links are unchanged: their authored label already wins display, and
that shape (a label hiding the address) is PrettyLink's documented contract,
not a bug.

Salvaged from PR #38213 by Phantomthedog (the fallbackLabel change and the
localhost/example.com render tests), reworked to keep the change scoped to
the bare-autolink shape and reshaped into an end-to-end
MarkdownTextContent test alongside the existing session/filelinks suites.
2026-09-26 18:00:17 -05:00
finn763
71b79122ca fix(desktop): hand the popped-out Browser its tab from shared storage (#119850)
A popped-out Browser window is a fresh renderer: no in-memory atoms cross
the window boundary, and no session ever pushes a rail scope into it (the
controller skips session/preview watching there), so its scoped previewTabs
view started empty and PreviewTilePane rendered null — the shell spawned but
never painted, matching the reported black window. Root cause is the
per-profile rail scoping from c996d1c088, not the GHSA window-open policy
the reporter suspected: the window/IPC handoff itself is fine.

Three coordinated moves in the store:

- adoptPersistedBrowserTab now reads every profile bucket (plus the
  pre-scoping single-array shape; the old decode returned [] for bucketed
  storage, so even the sibling URL sync was dead) and, when the tab is not
  in this renderer's view, re-homes the view onto the OWNING bucket instead
  of splicing the tab into 'default' — a splice would duplicate the popped
  tab into the primary profile's rail. When the tab IS present (the docked
  mirror on pop-out close), it adopts the newer URL/label as before.
- PreviewTilePane calls that adoption from a browser window when its tab is
  missing from the view.
- The persist subscriber no longer echoes module-init emissions back over
  storage: nanostores fires subscribe immediately, so every renderer used to
  clobber its un-adopted record (a legacy single-array store was wiped
  before pendingLegacyTabs could adopt it; a 'default'-only bucket store was
  removed the same way), and the view is now seeded from the renderer's own
  bucket — which also restores the primary profile's rail at boot, since
  setPreviewScope('default') early-returns on the initial viewKey.

Salvaged from PR #120110 by finn763 (diagnosis + adoption mechanism +
creation-emission guard, re-homed onto the owner bucket to avoid the
duplicate-bucket write.
EOF
)
2026-09-26 18:00:17 -05:00
Hermes Agent
b85406d5f3 fix(update): remove the npm logs dir even when the retry fails 2026-09-26 17:59:59 -05:00
Hermes Agent
625f1d8926 fix(update): retry node dependency preparation after ENOTEMPTY 2026-09-26 17:59:59 -05:00
Hermes Agent
b42474b0d1 style(desktop): blank lines around statements this PR adds (eslint warnings) 2026-09-26 17:59:52 -05:00
Hermes Agent
3836710cfa fix(desktop): give a pre-save sign-in the same jar its saved connection reads
The unknown-id shortcut gave any unsaved draft a private OAuth jar, so a
pre-save cloud sign-in wrote persist:hermes-remote-oauth-conn-<id> while the
saved cloud connection reads the shared persist:hermes-remote-oauth. Send the
draft's kind/authMode with the request and only grant a private jar to remote
OAuth drafts; everything else falls back to the legacy jar.
2026-09-26 17:59:52 -05:00