diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e5f9d095252..5b1ae34aa07 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -210,7 +210,7 @@ hermes-agent/
| `~/.hermes/skills/` | All active skills (bundled + hub-installed + agent-created) |
| `~/.hermes/memories/` | Persistent memory (MEMORY.md, USER.md) |
| `~/.hermes/state.db` | SQLite session database |
-| `~/.hermes/sessions/` | JSON session logs |
+| `~/.hermes/sessions/` | Gateway routing index (`sessions.json`), request-dump breadcrumbs, gateway `*.jsonl` transcripts, and (optionally) per-session JSON snapshots when `sessions.write_json_snapshots: true` is set. The per-session snapshots are off by default; state.db is canonical. |
| `~/.hermes/cron/` | Scheduled job data |
| `~/.hermes/whatsapp/session/` | WhatsApp bridge credentials |
@@ -239,7 +239,7 @@ User message → AIAgent._run_agent_loop()
- **Self-registering tools**: Each tool file calls `registry.register()` at import time. `model_tools.py` triggers discovery by importing all tool modules.
- **Toolset grouping**: Tools are grouped into toolsets (`web`, `terminal`, `file`, `browser`, etc.) that can be enabled/disabled per platform.
-- **Session persistence**: All conversations are stored in SQLite (`hermes_state.py`) with full-text search and unique session titles. JSON logs go to `~/.hermes/sessions/`.
+- **Session persistence**: All conversations are stored in SQLite (`hermes_state.py`) with full-text search and unique session titles. Per-session JSON snapshots in `~/.hermes/sessions/` were superseded by the SQLite store and are off by default; opt back in with `sessions.write_json_snapshots: true` if you have external tooling that consumes the JSON files directly.
- **Ephemeral injection**: System prompts and prefill messages are injected at API call time, never persisted to the database or logs.
- **Provider abstraction**: The agent works with any OpenAI-compatible API. Provider resolution happens at init time (Nous Portal OAuth, OpenRouter API key, or custom endpoint).
- **Provider routing**: When using OpenRouter, `provider_routing` in config.yaml controls provider selection (sort by throughput/latency/price, allow/ignore specific providers, data retention policies). These are injected as `extra_body.provider` in API requests.
diff --git a/agent/agent_init.py b/agent/agent_init.py
index e0846291ad6..c39712d4d02 100644
--- a/agent/agent_init.py
+++ b/agent/agent_init.py
@@ -901,7 +901,19 @@ def init_agent(
hermes_home = get_hermes_home()
agent.logs_dir = hermes_home / "sessions"
agent.logs_dir.mkdir(parents=True, exist_ok=True)
- agent.session_log_file = agent.logs_dir / f"session_{agent.session_id}.json"
+ # Per-session JSON snapshot writer (~/.hermes/sessions/session_{sid}.json)
+ # is opt-in via sessions.write_json_snapshots (default False). state.db
+ # is canonical — the snapshot is only useful for external tooling that
+ # reads the JSON files directly. See run_agent._save_session_log.
+ agent._session_json_enabled = False
+ try:
+ from hermes_cli.config import load_config as _load_sess_cfg
+ _sess_cfg = (_load_sess_cfg().get("sessions") or {})
+ agent._session_json_enabled = bool(_sess_cfg.get("write_json_snapshots", False))
+ except Exception:
+ pass
+ # logs_dir is retained unconditionally for request_dump_*.json (debug
+ # breadcrumb path written by agent_runtime_helpers.dump_api_request_debug).
# Track conversation messages for session logging
agent._session_messages: List[Dict[str, Any]] = []
diff --git a/agent/conversation_compression.py b/agent/conversation_compression.py
index a3a9ba1d6fb..cd1b133fa4a 100644
--- a/agent/conversation_compression.py
+++ b/agent/conversation_compression.py
@@ -387,8 +387,6 @@ def compress_context(
_SESSION_ID.set(agent.session_id)
except Exception:
pass
- # Update session_log_file to point to the new session's JSON file
- agent.session_log_file = agent.logs_dir / f"session_{agent.session_id}.json"
agent._session_db_created = False
agent._session_db.create_session(
session_id=agent.session_id,
diff --git a/agent/conversation_loop.py b/agent/conversation_loop.py
index 41eb2d730f1..caac0d3e8f2 100644
--- a/agent/conversation_loop.py
+++ b/agent/conversation_loop.py
@@ -1454,7 +1454,6 @@ def run_conversation(
}
messages.append(continue_msg)
agent._session_messages = messages
- agent._save_session_log(messages)
restart_with_length_continuation = True
break
@@ -3086,7 +3085,6 @@ def run_conversation(
if not agent.quiet_mode:
agent._vprint(f"{agent.log_prefix}↻ Codex response incomplete; continuing turn ({agent._codex_incomplete_retries}/3)")
agent._session_messages = messages
- agent._save_session_log(messages)
continue
agent._codex_incomplete_retries = 0
@@ -3411,7 +3409,6 @@ def run_conversation(
# Save session log incrementally (so progress is visible even if interrupted)
agent._session_messages = messages
- agent._save_session_log(messages)
# Continue loop for next response
continue
@@ -3578,7 +3575,6 @@ def run_conversation(
interim_msg["_thinking_prefill"] = True
messages.append(interim_msg)
agent._session_messages = messages
- agent._save_session_log(messages)
continue
# ── Empty response retry ──────────────────────
@@ -3712,7 +3708,6 @@ def run_conversation(
}
messages.append(continue_msg)
agent._session_messages = messages
- agent._save_session_log(messages)
continue
codex_ack_continuations = 0
diff --git a/agent/transports/chat_completions.py b/agent/transports/chat_completions.py
index 7edb69e42c7..fa36301bd81 100644
--- a/agent/transports/chat_completions.py
+++ b/agent/transports/chat_completions.py
@@ -112,17 +112,31 @@ class ChatCompletionsTransport(ProviderTransport):
def convert_messages(
self, messages: list[dict[str, Any]], **kwargs
) -> list[dict[str, Any]]:
- """Messages are already in OpenAI format — sanitize Codex leaks only.
+ """Messages are already in OpenAI format — strip internal fields
+ that strict chat-completions providers reject with HTTP 400/422.
- Strips Codex Responses API fields (``codex_reasoning_items`` /
- ``codex_message_items`` on the message, ``call_id``/``response_item_id``
- on tool_calls) that strict chat-completions providers reject with 400/422.
+ Strips:
+
+ - Codex Responses API fields: ``codex_reasoning_items`` /
+ ``codex_message_items`` on the message, ``call_id`` /
+ ``response_item_id`` on ``tool_calls`` entries.
+ - ``tool_name`` on tool-result messages — written by
+ ``make_tool_result_message()`` for the SQLite FTS index, but not
+ part of the Chat Completions schema. Strict providers (Fireworks,
+ Moonshot/Kimi) reject any payload containing it with
+ ``Extra inputs are not permitted, field: 'messages[N].tool_name'``.
+ Permissive providers (OpenRouter, MiniMax) silently ignore the
+ field, which masked the bug for months.
"""
needs_sanitize = False
for msg in messages:
if not isinstance(msg, dict):
continue
- if "codex_reasoning_items" in msg or "codex_message_items" in msg:
+ if (
+ "codex_reasoning_items" in msg
+ or "codex_message_items" in msg
+ or "tool_name" in msg
+ ):
needs_sanitize = True
break
tool_calls = msg.get("tool_calls")
@@ -145,6 +159,7 @@ class ChatCompletionsTransport(ProviderTransport):
continue
msg.pop("codex_reasoning_items", None)
msg.pop("codex_message_items", None)
+ msg.pop("tool_name", None)
tool_calls = msg.get("tool_calls")
if isinstance(tool_calls, list):
for tc in tool_calls:
diff --git a/apps/dashboard/package-lock.json b/apps/dashboard/package-lock.json
index d617db6e5b3..67801725780 100644
--- a/apps/dashboard/package-lock.json
+++ b/apps/dashboard/package-lock.json
@@ -9,7 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@hermes/shared": "file:../shared",
- "@nous-research/ui": "0.14.0",
+ "@nous-research/ui": "^0.14.2",
"@observablehq/plot": "^0.6.17",
"@react-three/fiber": "^9.6.0",
"@tailwindcss/vite": "^4.2.1",
@@ -1092,12 +1092,13 @@
}
},
"node_modules/@nous-research/ui": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@nous-research/ui/-/ui-0.14.0.tgz",
- "integrity": "sha512-tfpE6jGOxE5oVBab/dTSepOudy/+Xep3gJ6NCFriYJvdtQBGXcqsi4mCaVPiNNaS/ZFf4/10dnl/oJTb6DtLKg==",
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/@nous-research/ui/-/ui-0.14.2.tgz",
+ "integrity": "sha512-H3cMt2e0IpmcTNOmR6zVX+8ja48w4X4F/IFXhWCpaoVs8zKVRN12Ryb4RnX/ac8IrbUu6UsIds7ZtmXxPHcfdQ==",
"license": "MIT",
"dependencies": {
"@nanostores/react": "^1.1.0",
+ "@radix-ui/react-checkbox": "^1.3.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"nanostores": "^1.3.0",
@@ -1177,13 +1178,20 @@
}
}
},
- "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "node_modules/@radix-ui/react-checkbox": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz",
+ "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-slot": "1.2.3"
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-presence": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/react-use-previous": "1.1.1",
+ "@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@@ -1200,24 +1208,6 @@
}
}
},
- "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@@ -1275,47 +1265,6 @@
}
}
},
- "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-slot": "1.2.3"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-id": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
@@ -1366,47 +1315,6 @@
}
}
},
- "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-slot": "1.2.3"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-portal": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.10.tgz",
@@ -1431,6 +1339,47 @@
}
}
},
+ "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
+ "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
+ "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
@@ -1456,12 +1405,12 @@
}
},
"node_modules/@radix-ui/react-primitive": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
- "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-slot": "1.2.4"
+ "@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
@@ -1479,9 +1428,9 @@
}
},
"node_modules/@radix-ui/react-slot": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
- "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
@@ -1554,47 +1503,6 @@
}
}
},
- "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-slot": "1.2.3"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
@@ -1680,6 +1588,21 @@
}
}
},
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
+ "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
@@ -1739,47 +1662,6 @@
}
}
},
- "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-slot": "1.2.3"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
@@ -3904,9 +3786,9 @@
"license": "ISC"
},
"node_modules/enhanced-resolve": {
- "version": "5.21.5",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz",
- "integrity": "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==",
+ "version": "5.21.6",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz",
+ "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json
index 8fc25f9d030..cb8271a9da9 100644
--- a/apps/dashboard/package.json
+++ b/apps/dashboard/package.json
@@ -11,7 +11,7 @@
},
"dependencies": {
"@hermes/shared": "file:../shared",
- "@nous-research/ui": "0.14.0",
+ "@nous-research/ui": "^0.14.2",
"@observablehq/plot": "^0.6.17",
"@react-three/fiber": "^9.6.0",
"@tailwindcss/vite": "^4.2.1",
diff --git a/apps/dashboard/src/components/ModelPickerDialog.tsx b/apps/dashboard/src/components/ModelPickerDialog.tsx
index 22b2cb1bce8..d01a46b01a0 100644
--- a/apps/dashboard/src/components/ModelPickerDialog.tsx
+++ b/apps/dashboard/src/components/ModelPickerDialog.tsx
@@ -1,6 +1,8 @@
import { Button } from "@nous-research/ui/ui/components/button";
+import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
import { ListItem } from "@nous-research/ui/ui/components/list-item";
import { Spinner } from "@nous-research/ui/ui/components/spinner";
+import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import type { GatewayClient } from "@/lib/gatewayClient";
import { Check, Search, X } from "lucide-react";
@@ -283,15 +285,22 @@ export function ModelPickerDialog(props: Props) {
Saves to config.yaml — applies to new sessions.
) : (
-
+
+
+
)}
diff --git a/apps/dashboard/src/components/ui/checkbox.tsx b/apps/dashboard/src/components/ui/checkbox.tsx
deleted file mode 100644
index fa9f0098a00..00000000000
--- a/apps/dashboard/src/components/ui/checkbox.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { cn } from "@/lib/utils";
-import { Check } from "lucide-react";
-
-interface CheckboxProps
- extends Omit
, "type"> {
- label?: React.ReactNode;
-}
-
-export function Checkbox({
- className,
- label,
- id,
- checked,
- defaultChecked,
- ...props
-}: CheckboxProps) {
- // Support both controlled (checked prop) and uncontrolled (defaultChecked) usage.
- // For visual rendering, prefer `checked` if provided; otherwise fall back to defaultChecked.
- const isChecked = checked ?? defaultChecked ?? false;
-
- return (
-
- );
-}
diff --git a/apps/dashboard/src/index.css b/apps/dashboard/src/index.css
index e9818174e02..854c528cddf 100644
--- a/apps/dashboard/src/index.css
+++ b/apps/dashboard/src/index.css
@@ -1,4 +1,11 @@
@import 'tailwindcss';
+/* `fonts.css` must come BEFORE `globals.css`: as of @nous-research/ui 0.14.x,
+ `globals.css` only declares the `--font-*` CSS variables (Collapse, Rules
+ Compressed/Expanded, Mondwest). The `@font-face` registrations live in
+ `fonts.css`, so without this import the DS variables resolve to font
+ families the browser never loads and components fall back to a system
+ stack (Tabs, Segmented, Typography, Buttons, etc. all look unstyled). */
+@import '@nous-research/ui/styles/fonts.css';
@import '@nous-research/ui/styles/globals.css';
/* Scan the published design-system bundle so its utility classes survive
diff --git a/apps/dashboard/src/pages/AnalyticsPage.tsx b/apps/dashboard/src/pages/AnalyticsPage.tsx
index 5d8ff706948..0fecc9c4ad2 100644
--- a/apps/dashboard/src/pages/AnalyticsPage.tsx
+++ b/apps/dashboard/src/pages/AnalyticsPage.tsx
@@ -439,7 +439,7 @@ export default function AnalyticsPage() {
);
setEnd(
showTokens === false ? null : (
-
+
{PERIODS.map((p) => (