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) => ( , @@ -266,7 +280,9 @@ export default function ProfilesPage() {
e.target === e.currentTarget && setCreateModalOpen(false)} + onClick={(e) => + e.target === e.currentTarget && setCreateModalOpen(false) + } role="dialog" aria-modal="true" aria-labelledby="create-profile-title" @@ -313,12 +329,22 @@ export default function ProfilesPage() {

- setCloneFromDefault(e.target.checked)} - label={t.profiles.cloneFromDefault} - /> +
+ + setCloneFromDefault(checked === true) + } + /> + + +