Commit Graph

19755 Commits

Author SHA1 Message Date
Brooklyn Nicholson
73b8847d7b fix(desktop): toggle every pane off the tree, not off its own boolean
The terminal fix was only one instance. An audit of the other pane toggles
found ⌘G and ⌘J diverging the same way, proven with a probe: with review
stacked behind files in the right column, or either pane inside a minimized
zone, the store reads open while nothing is on screen, so the press
re-asserts a value it already held and the key does nothing.

isPaneVisible / togglePaneVisible replace the tool-panel-only pair and now
back every toggle. Close still routes through closeTreePane, so each pane
keeps its own semantics: a tool panel collapses to its rail, files and
review close through their store, anything else is dismissed.

files and review were bound with a closer and no opener, so the boolean went
stale as soon as anything but the toggle revealed them. bindPaneVisibility
moves into the tree store beside bindToolPaneCollapse, documents the two as a
pair, and both panes now pass both halves. Keeping the binding in the store
also means the tests drive the real function — the earlier copy in the test
file passed with the fix reverted, which is how the missing opener survived
the first pass.

setTreePaneHidden keeps its quiet path: a reactive unhide (a cwd arriving)
must not front or un-minimize over what the user is looking at. Only user
intent goes through the reveal path.
2026-08-01 01:17:45 -05:00
Brooklyn Nicholson
16431b8ab2 test(desktop): cover the tool-panel toggle and hidden-header regressions 2026-07-31 23:49:28 -05:00
Brooklyn Nicholson
aca571ef49 feat(desktop): let a tool panel shrink to its collapsed header
The terminal and logs carried a 7.5rem minHeight, so dragging the seam down
jammed against a floor with a sliver of unusable terminal still showing.

Tool-panel zones now floor at COLLAPSED_ZONE_PX (the h-7 header strip)
instead of the generic 80px, and releasing at that floor minimizes the zone
rather than persisting the sliver — it folds to its collapsed header,
vertical rail or horizontal strip depending on the parent axis. The sliver
size is never written, so restoring returns the height it had before.
2026-07-31 23:49:28 -05:00
Brooklyn Nicholson
4802d2d88d fix(desktop): keep a hidden zone header hidden
Two independent clobbers threw the choice away, so "hide header" never
survived a close/reopen cycle.

normalize() deleted headerHidden whenever a zone dropped to one pane, on
the grounds that a lone zone is headerless by default. It is — but the flag
is the user's standing preference for that zone, not a redundant value, and
dropping it meant the bar returned the moment a pane rejoined (close the
stacked logs pane, toggle it back).

Re-adoption then pinned headerHidden false unconditionally. That is right
for a pane arriving somewhere new, where zero chrome leaves no handle to
drag or close, and wrong for a zone whose bar the user deliberately hid.
Adoption now carries the destination zone's own setting, read before the
insert since insertAtGroup pins the flag itself on a center drop.
2026-07-31 23:49:11 -05:00
Brooklyn Nicholson
de46fb0365 fix(desktop): make the terminal toggle work wherever the terminal sits
Dragging the terminal to the bottom stacks logs into its zone, and every
tool-panel toggle broke in that stack.

Boot revealed instead of leaving the tree alone: bindPaneCollapse ran
setPaneCollapsed(id, !open), and `false` there fronts the pane. Logs binds
last, so it stole the active tab from the persisted tree. Ctrl-` then asked
to collapse a terminal that was no longer the active tab, the shared-zone
branch declined by design, and the key did nothing until the stack was
broken up. Boot now only ever collapses.

The toggles also asked the wrong question. !$terminalTakeover.get() flips a
boolean that has no idea what is on screen, so once anything else moved the
pane the press spent itself re-asserting a value the store already held.
toggleToolPane derives from the tree instead, and ctrl-`, Cmd+J's terminal
fallback, the statusbar button and the logs palette row all route through
it. The terminal cycle/close keys drop the same stale boolean.

bindPaneCollapse moves into the tree store as bindToolPaneCollapse so the
boot rule is testable against the real function rather than a copy.
2026-07-31 23:48:56 -05:00
brooklyn!
8555c6d67a Merge pull request #75822 from NousResearch/bb/cmdk-plugins
feat(desktop): open Plugins from Cmd+K
2026-07-31 22:55:28 -05:00
Brooklyn Nicholson
696c2e8265 feat(desktop): open Plugins from the command palette
Settings Plugins was missing from cmd+K Settings group. Add the deep-link
row next to the other non-config tabs so typing plugins lands on the page.
2026-07-31 22:49:07 -05:00
Austin Pickett
e444d16580 fix(vision): make desktop image uploads reachable from profile Docker sandboxes (#69575) (#75671)
* fix(vision): mount images/ upload dir into sandboxes and permit host read (#69575)

Desktop, clipboard, and PDF uploads land in the flat top-level
HERMES_HOME/images/ dir, but Docker sandboxes only mounted the cache/
subtree and the vision resolver only permitted host reads from the media
caches. So vision_analyze on any desktop-app upload failed under a Docker
backend with "not reachable inside the sandbox".

- Add ("images", "images") to _CACHE_DIRS so the uploads dir is bind-mounted
  into sandbox containers through the existing profile-scoped cache-mount and
  reverse-mapping mechanism.
- Add home/"images" to _media_cache_roots() so the non-local host-read
  allowlist permits reading uploads directly from the host filesystem.
- Cover the mount entry, the container path mapping, and the Docker-mode
  resolver read for a profile-scoped upload.

Co-authored-by: JonthanaHanh <92574114+JonthanaHanh@users.noreply.github.com>
Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>

* fix(tui_gateway): write image uploads under the session's profile home (#69575)

The attach RPCs (image.attach_bytes, clipboard.paste, pdf.attach) wrote
uploads to the gateway's module-cached launch home via _hermes_home/"images".
Those RPCs run before prompt.submit installs the session's profile HERMES_HOME
override, so in a multi-profile / root-gateway deployment the file landed in
the launch home while the sandbox mount and the vision host-read allowlist
both resolve the session profile's images/ at run time — the agent could
never see the upload it was handed.

Add _session_images_dir(session), which anchors the write on the session's
stored profile_home when present (matching the mount/read scope) and falls
back to the launch home otherwise. Route both write sites through it, keeping
per-profile isolation.

Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>

---------

Co-authored-by: JonthanaHanh <92574114+JonthanaHanh@users.noreply.github.com>
Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>
2026-07-31 17:43:37 -04:00
brooklyn!
4b60979dc1 Merge pull request #75583 from NousResearch/bb/hide-kanban-worker-sessions
Kanban worker runs stop appearing as chats in the sidebar
2026-07-31 14:09:01 -05:00
Brooklyn Nicholson
c2872cf53b fix(kanban): key the worker-session retag per board, not per database
The retag gate was global, so once one board reclaimed its legacy rows a
second board on the same state.db never got swept. Key the state_meta gate
on the workspaces root and skip reopening state.db on every spawn via an
in-process set. Align the dispatcher-spawn test with the worker's own
`kanban` source tag and cover the per-board gate.
2026-07-31 14:03:32 -05:00
Ben Barclay
4a8eeb5d1c fix(gateway): relay semantic thread rename — register eagerly, poll send-result feedback at fire time (#75581)
Staging re-test (2026-07-31, post-74482 image roll): auto-created
threads still stuck on their initial titles; connector telemetry shows
zero thread_rename ops. Root cause is an ordering flaw in the 74482
consume path: BOTH the title-callback registration gate and the
schedule gate read the send-result feedback cache
(_relay_auto_thread_info) — but registration runs BEFORE delivery on
the non-streaming lane, and the auto-title thread races delivery even
when registration survives. The cache read can only succeed AFTER the
connector answers the send, so the rename lane deterministically
disqualified itself on the title turn.

Fix — decide shape early, facts late:
- New _is_relay_discord_channel_lane: SHAPE-only predicate (relay
  Discord channel event, no thread) used by the registration and
  schedule gates; no cache read before delivery.
- _rename_discord_auto_thread_for_session_title: on the relay lane,
  poll the adapter's feedback cache (0.5s ticks, ≤10s) — delivery is
  typically right behind the title. True miss (connector didn't
  auto-thread: policy off, DM, send failed) no-ops exactly as before.

Tests: shape-gate matrix; late-arriving feedback -> rename fires with
only_if_current_name guard; never-arriving feedback -> no-op. Relay
suite 174 passed.
2026-07-31 11:55:05 -07:00
hermes-seaeye[bot]
17e5f7244a fmt(js): npm run fix on merge (#75582)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-31 18:54:27 +00:00
Brooklyn Nicholson
805c483ca5 test(kanban): cover worker session tagging and retag 2026-07-31 13:53:04 -05:00
Brooklyn Nicholson
e41d2029b7 fix(sessions): keep kanban worker runs out of the session lists
Exclude the source from the desktop sidebar and project tree, the TUI
resume picker, session_search, and the CLI session listings.
2026-07-31 13:53:04 -05:00
Brooklyn Nicholson
9fe36aecb1 fix(state): reclaim kanban worker rows already on disk
Retag pre-tag `cli` rows whose cwd sits under the board's workspaces
root, gated once per database via state_meta.
2026-07-31 13:53:04 -05:00
Brooklyn Nicholson
1d58e7d231 fix(kanban): tag worker sessions with their own source
Workers spawn as `hermes chat -q "work kanban task <id>"` without
HERMES_SESSION_SOURCE, so every attempt persisted as an untitled `cli`
row. Tag them `kanban` and register it as a local, non-messaging
surface.
2026-07-31 13:53:04 -05:00
brooklyn!
890e3c34c4 Merge pull request #75563 from NousResearch/bb/logs-tab-close
Close the terminal and logs tabs like any other tab
2026-07-31 13:44:49 -05:00
Austin Pickett
56f4b1a20a fix(tui): expand collapsed paste tokens before submission (#75565)
* fix(tui): expand collapsed paste tokens before submission

* fix(tui): show resolved interpolation, not raw {!...}, with paste tokens

The interpolation branch of dispatchSubmission passed the pre-interpolation
composer text as the transcript display, so a paste token combined with a
visible {!...} rendered the literal interpolation syntax instead of the
resolved output main shows today. Pass interpolate()'s resolved text as the
display override: it still carries the compact paste label while the model
payload expands the paste. Add a dispatch-level regression for the combined
interpolation + collapsed-paste route.

Co-authored-by: teknium1 <teknium1@users.noreply.github.com>

---------

Co-authored-by: UltraInstinct0x <gokhansarapevi@gmail.com>
Co-authored-by: teknium1 <teknium1@users.noreply.github.com>
2026-07-31 14:44:25 -04:00
Brooklyn Nicholson
2286ee58c5 fix(desktop): drop imports left unused by the shared close routing 2026-07-31 13:36:53 -05:00
Brooklyn Nicholson
c25e4fe687 fix(desktop): close the terminal and logs tabs like any other tab
Two things made a tool panel tab feel unclosable.

Cmd-W was a dead key over the terminal and the logs pane. The keyboard
close ladder resolved its target with focusedSessionGroup, which only
matches zones hosting a CHAT strip, so a focused tool panel fell through
every rung and Cmd-W emptied the main tab instead. Add a tool rung that
resolves through the same hover/focus ladder the number keys use.

Right-click Close was missing or inert. The zone menu's target was only
resolved by the tab strip's own onContextMenu, so a right-click anywhere
else in the zone (pane body, collapsed rail, edit veil) reused the
PREVIOUS target -- landing on the uncloseable workspace dropped Close
from the menu entirely. Resolve the target on the zone instead, so every
surface that opens the menu names the chip under the pointer.

Close on a tool panel now takes the tab out of the strip and syncs its
owning store, so the ctrl-backtick toggle and the Cmd-K row stay
truthful and bring the pane back; the toggle's open path reveals
(un-dismiss + re-adopt) rather than un-collapsing a pane that has left
the tree.
2026-07-31 13:21:57 -05:00
Brooklyn Nicholson
cfae306ab6 Revert "fix(desktop): tool panel tab ✕ can close and re-open"
This reverts commit dd48d9a816.
2026-07-31 13:07:51 -05:00
ethernet
6ecd335aa8 Merge pull request #75037 from NousResearch/sec-fixes
fix(sec): patch vulnerable deps + add publication-age floors and npm script allow-list

Co-authored-by: Kingsley Wong <7207924+datanerdie@users.noreply.github.com>
Co-authored-by: viky <vikyw89@gmail.com>
Co-authored-by: FT_IOxCS <237263164+ft-ioxcs@users.noreply.github.com>
Co-authored-by: 方明元 <fmy3@qq.com>
Co-authored-by: Yorkstone Supplies <58149681+sycamoregroupltd@users.noreply.github.com>
Co-authored-by: Steven Cuz Leath <Steven.Leath@gmail.com>
Co-authored-by: Kyle French <248366920+Dadmin88@users.noreply.github.com>
Co-authored-by: Eugeniusz Gilewski <egilewski@egilewski.com>
Co-authored-by: Christopher Gara <79837758+christopherrobin88@users.noreply.github.com>
Co-authored-by: LironTTG <147833337+LironTTG@users.noreply.github.com>
Co-authored-by: Austin Porada <bbasketballer75@gmail.com>
Co-authored-by: cresslank <9219265+cresslank@users.noreply.github.com>
Co-authored-by: Ion Mudreac <mudreac@gmail.com>
Co-authored-by: martinramos002 <262243228+martinramos002-bot@users.noreply.github.com>
Co-authored-by: Sensie-Agents <agents@joinsensie.com>
Co-authored-by: alexwill87 <173086651+alexwill87@users.noreply.github.com>
Co-authored-by: BullishMomentum56 <218643122+BullishMomentum56@users.noreply.github.com>
Co-authored-by: pintadoai <240097310+pintadoai@users.noreply.github.com>
Co-authored-by: Alfred Sahlberg <dinmail@gmail.com>
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
Co-authored-by: Richard Ham <richard.ham@live.com>
Co-authored-by: jrcrittenden <jrcrittenden@gmail.com>
Co-authored-by: 峯岸 亮 <1920071390@campus.ouj.ac.jp>
Co-authored-by: Marcus Martini <6473852+napoleonmm83@users.noreply.github.com>
2026-07-31 14:07:32 -04:00
brooklyn!
0324849fe4 Merge pull request #61173 from NousResearch/bb/desktop-kanban
feat(desktop): Kanban — the founding plugin on the desktop SDK
2026-07-31 13:00:10 -05:00
ethernet
bc49e98143 docs(contributors): map salvaged supply-chain PR authors
scripts/contributor_audit.py resolves Co-authored-by trailers through
contributors/emails/, so a co-author with a plain (non-noreply) email and
no mapping file silently drops out of the generated release notes.

This PR consolidates ~30 open dependency/supply-chain PRs and credits their
authors as co-authors on the merge commit. Nine of those emails had no
mapping. Added via scripts/add_contributor.py, one file per email:

  vikyw89@gmail.com          -> vikyw89              (#50902)
  fmy3@qq.com                -> superafun            (#60201)
  Steven.Leath@gmail.com     -> Leathal1             (#69711)
  bbasketballer75@gmail.com  -> bbasketballer75      (#69864, #73857)
  mudreac@gmail.com          -> mudrii               (#66871, #63099)
  agents@joinsensie.com      -> Sensie-agents        (#65150)
  dinmail@gmail.com          -> sahlbergalfred4-lgtm (#70003)
  richard.ham@live.com       -> zebadee2kk           (#50052)
  jrcrittenden@gmail.com     -> jrcrittenden         (#28749)

egilewski@egilewski.com, sunsky.lau@gmail.com and 1920071390@campus.ouj.ac.jp
were already mapped. Every other co-author uses a GitHub id+login noreply
address, which auto-resolves and needs no file.

tests/scripts/test_contributor_map.py passes.
2026-07-31 13:56:54 -04:00
Brooklyn Nicholson
dd48d9a816 fix(desktop): tool panel tab ✕ can close and re-open
The logs (and terminal) tab ✕ dismissed the pane from the layout but
never synced the owning store — so the ⌘K toggle was stale and its open
listener called setPaneCollapsed, a no-op when the pane isn't in the
tree. The tab was gone with no way back short of a layout reset.

Route the tab ✕ through closeCollapsePane (dismiss + store sync) so the
toggle stays truthful, and make bindPaneCollapse's open listener call
revealTreePane (un-dismiss + re-adopt) instead of setPaneCollapsed.
2026-07-31 12:54:41 -05:00
ethernet
e803c5aeac fix(npm): self-upgrade a managed npm when engines.npm rejects it
The repo's .npmrc sets engine-strict=true and package.json pins
engines.npm, so an npm outside that range aborts every npm ci /
npm install we run inside the checkout:

    npm error code EBADENGINE
    npm error notsup Required: {"npm":"<11.10.0 || >=12.0.0"}
    npm error notsup Actual:   {"npm":"11.10.0"}

Our callers made that worse: _run_npm_install_deterministic sees
`npm ci` fail and falls through to `npm install`, which fails
identically, so the user got a buried EBADENGINE and no remedy.

React to the failure instead of predicting it. npm states the
required range in its own error, so there is no need for a version
probe on the happy path or a semver range matcher — the recovery
reads the constraint out of the output it just produced, upgrades,
and retries once.

Scope is deliberately narrow. Hermes only upgrades an npm inside its
own managed Node tree ($HERMES_HOME/node), installing with --prefix
so bin/npm keeps resolving to the upgraded lib/node_modules/npm; a
managed install writes prefix=~/.local into node/etc/npmrc, so
without the override the "upgrade" would land elsewhere while the
managed npm stayed stale. A system / nvm / brew / Nix npm belongs to
the user, so that case prints the exact command and lets the original
failure stand.

The upgrade runs from a temp cwd with npm_config_min_release_age=0,
otherwise the checkout's own min-release-age gate would refuse the
npm release we need.

_run_npm_install_deterministic's capture_output=False callers (the
desktop install) streamed npm output and returned stderr=None, which
would leave the recovery nothing to read — stderr is now teed, so
live output is unchanged and the text stays inspectable.

Verified end to end against real npm binaries on copies of a managed
tree: managed npm 11.10.0 -> EBADENGINE -> upgraded to 12.0.2 ->
retry exits 0; a foreign npm 11.10.0 hard-fails with the manual
command and is left untouched.
2026-07-31 13:42:04 -04:00
ethernet
3975e9d753 fix(npm): npm 11.10-12.0 support min-release-age but NOT min-release-age-exclude.
that would break stuff :)
2026-07-31 13:42:04 -04:00
ethernet
9c85d68bcc fix(js): update vite 2026-07-31 13:42:03 -04:00
ethernet
fabc2d7d33 fix(js): hoist eslint shared devDeps to workspace root 2026-07-31 13:42:03 -04:00
ethernet
f7082c5172 fix(sec): pin exact npm package versions in website/, lock. 2026-07-31 13:42:03 -04:00
ethernet
94c27cae60 fix(sec): add min-release-age = 2 wks in website/.npmrc 2026-07-31 13:42:03 -04:00
ethernet
a4ffe39f73 fix(sec): add allowScripts to website/ 2026-07-31 13:42:03 -04:00
ethernet
88aed1b797 feat(sec): add exclude-newer = 14 days to uv.lock
disabled for 2 packages that are too new
2026-07-31 13:42:03 -04:00
ethernet
43f18cca81 feat(sec): add min-release-age = 2 wks in .npmrc
has some exclusions for packages with recent security fixes, which are
to be removed in >= 2wks
2026-07-31 13:42:03 -04:00
ethernet
515e88a80f fix(sec): pin exact npm package versions everywhere 2026-07-31 13:42:03 -04:00
ethernet
2158f2a5ad fix(sec): add allowScripts 2026-07-31 13:42:03 -04:00
ethernet
f976284245 nix: update nixpkgs, update nodejs to 26
brings npm 12 :)
2026-07-31 13:42:03 -04:00
ethernet
390a1771b2 fix(sec): update npm deps to resolve npm audit warnings
pin brace-expansion to 5.0.8
update concurrently to 10.0.4
update electron-builder to 26.15.3
update eslint to 10.8.0
update eslint-plugin-perfectionist to 5.10.0
update @assistant-ui/react to 0.15.0
update @assistant-ui/react-streamdown to 0.3.8
update radix-ui to 1.6.7
update react-router-dom to react-router@8.3.0 - react-router-dom is no longer a standalone package, it just reexports react-router
remove @radix-ui/react-slot: we import this from `radix-ui`
remove eslint-plugin-react: we imported it, but never actually used it!
2026-07-31 13:42:03 -04:00
ethernet
1f70e7ee7e update tornado to 6.5.7
tornado 6.5.5 has 7 known vulnerabilities:
GHSA-pw6j-qg29-8w7f
PYSEC-2026-3387
PYSEC-2026-3388
PYSEC-2026-3389

they're fixed in >= 6.5.7
2026-07-31 13:42:03 -04:00
ethernet
ea266a6061 fix(sec): update pynacl to 1.6.2
pynacl 1.5.0 has 1 known vulnerability:
PYSEC-2026-3002
it's fixed in >=1.6.2
2026-07-31 13:42:03 -04:00
ethernet
09da075505 fix(sec): update pygments to 2.20.0
pygments 2.19.2 has 1 known vulnerability:
PYSEC-2026-2987
it's fixed in >= 2.20.0
2026-07-31 13:42:03 -04:00
ethernet
f8d032588c fix(sec): update pytest to 9.1.1
pytest 9.0.2 has 1 known vulnerability:
GHSA-6w46-j5rx-g56g
it's fixed in >= 9.0.3
2026-07-31 13:42:03 -04:00
ethernet
eeab0a251d fix(sec): update pydantic-settings to 2.14.2
pydantic-settings 2.13.1 has 1 known vulnerability:
GHSA-4xgf-cpjx-pc3j

it's fixed in >= 2.14.2
2026-07-31 13:42:03 -04:00
ethernet
bb2539ea16 fix(sec): update pyasn1 to 0.6.4
pyasn1 0.6.3 has 5 known vulnerabilities:

GHSA-8ppf-4f7h-5ppj
GHSA-hm4w-wwcw-mr6r
PYSEC-2026-3455
PYSEC-2026-3456
PYSEC-2026-3457

they're fixed in >= 0.6.4
2026-07-31 13:42:03 -04:00
ethernet
abcd213504 fix(sec): update pillow to 12.3.0
pillow 12.2.0 has 26 known vulnerabilities:

GHSA-45hq-cxwh-f6vc
GHSA-4x4j-2g7c-83w6
GHSA-5x94-69rx-g8h2
GHSA-62p4-gmf7-7g93
GHSA-6r8x-57c9-28j4
GHSA-8v84-f9pq-wr9x
GHSA-9hw9-ch79-4vh6
GHSA-fj7v-r99m-22gq
GHSA-jjj6-mw9f-p565
GHSA-pg7v-jwj7-p798
GHSA-phj9-mv4w-65pm
GHSA-vjc4-5qp5-m44j
GHSA-xj96-63gp-2gmr
PYSEC-2026-2253
PYSEC-2026-2254
PYSEC-2026-2255
PYSEC-2026-2256
PYSEC-2026-2257
PYSEC-2026-3451
PYSEC-2026-3452
PYSEC-2026-3453
PYSEC-2026-3454
PYSEC-2026-3493
PYSEC-2026-3494
PYSEC-2026-3495
PYSEC-2026-3496

they're fixed in >= 12.3.0
2026-07-31 13:42:03 -04:00
ethernet
0945cc5b52 fix(sec): update msgpack to 1.2.1
msgpack 1.1.2 has 1 known vulnerability: GHSA-6v7p-g79w-8964

it's fixed in >= 1.2.1
2026-07-31 13:42:03 -04:00
ethernet
a7efeb0829 fix(sec): update mcp to 1.28.1
mcp 1.26.0 has 3 known vulnerabilities: PYSEC-2026-3481,
PYSEC-2026-3482, PYSEC-2026-3483

they're fixed in >= 1.28.1
2026-07-31 13:42:03 -04:00
brooklyn!
8f8bee94f7 Merge pull request #75547 from NousResearch/bb/statusbar-off-default
Statusbar off by default
2026-07-31 12:18:33 -05:00
Brooklyn Nicholson
d399c1644b fix(desktop): statusbar off by default
The statusbar is now opt-in. Existing users with a stored preference
keep their choice; new users get a clean bottom edge. The way back is
the view.toggleStatusbar keybind or the ⌘K row, unchanged.
2026-07-31 12:11:23 -05:00
Teknium
afc54ca806 feat(models): add deepseek/deepseek-v4-flash-0731 to Nous portal and OpenRouter catalogs
Dated snapshot of deepseek-v4-flash, live on both provider endpoints
(verified via OpenRouter /api/v1/models and Nous portal /v1/models).
Context (1M via deepseek-v4-flash prefix match), reasoning stale-timeout
floor (600s), and pricing (official_models_api live billing on both
routes) all resolve without new entries. model-catalog.json regenerated
via scripts/build_model_catalog.py.
2026-07-31 09:43:03 -07:00