When a reverse proxy (e.g., Tailscale Serve, nginx) closes the SSE
connection after an idle timeout, the reader returns { done: true }
cleanly. Previously this was treated as a successful completion, causing
the thinking indicator to disappear and a retry button to appear with
no error message — silently discarding any in-progress backend work.
Now: if the stream ends without a 'done' event AND no response text was
received while in 'accepted' or 'active' phase, call markFailed() with
a clear message instead of finishStream(). This surfaces an error toast
and persists the error state so the user knows what happened.
Fixes#512.
Validation: pnpm build passes, use-streaming-message tests (4/4) pass.
Fixes#505 and #506.
#505 — Assistant response disappears after auto-compaction:
- Capture the just-completed assistant message from the realtime buffer
BEFORE clearing it and invalidating the query cache.
- After background refetch, if compaction dropped the message count and
the assistant message is no longer present, re-inject it.
#506 — Submitted prompt reappears after assistant response:
- Update isOptimisticUserMessage to return false for sent/done messages,
preventing confirmed messages from being re-persisted as pending.
- Clear __optimisticId in onStarted callback so the message is no longer
treated as optimistic after server confirmation.
Salvaged from contaminated PR #524 (which included unrelated HermesWorld
game assets). Only the targeted chat fixes are included here.
Validation: pnpm build passes. Pre-existing test failures unrelated.
* fix(conductor): mobile rendering — add overflow-y-auto, remove justify-center, responsive OfficeView
Three rendering bugs fixed in the Conductor component:
1. Missing overflow-y-auto on active, preview, and complete phase
containers — content was silently clipped instead of scrolling
on mobile viewports.
2. justify-center on the active phase flex column main container
fought with natural top-to-bottom flow when content overflowed.
3. OfficeView fixed at h-[360px] on mobile — changed to
max-h-[clamp(200px,40vh,360px)] so it adapts to viewport height.
Fixes#445
* fix(conductor): home page header badge truncation and mission row mobile layout
- Replaced absolute-positioned action buttons with flex layout using
flex-1 spacers — prevents the Conductor badge from overlapping with
the action buttons on narrow screens.
- Added truncate to badge text and shrink-0 to the green dot for
graceful overflow.
- Hid token count column on mobile in recent missions rows to give
the mission title more room (reduces truncation).
- Reduced gap and fixed-width column sizes slightly for tighter mobile
layout.
* fix(conductor): handle native-swarm mode in hook — was falling through to dashboard logic with null session key
The ConductorSpawnResponse type declared native-swarm mode but the
sendMission handler had no case for it. When the server returned
mode: 'native-swarm', the hook fell through to the dashboard fallback
with null sessionKey/sessionKeyPrefix/missionId/jobId, throwing a
generic error.
Now native-swarm is handled with its own branch that:
- Sets missionId and jobId for mission status polling
- Uses missionId as the orchestrator session key proxy
- Sets descriptive plan text about swarm workers
- Immediately transitions to running phase
Also added the assignments field to ConductorSpawnResponse type.
* fix(conductor): infinite retry on mission status query — native-swarm missions could get stuck
The missionStatusQuery used default react-query retry (3 attempts),
which could exhaust before the SwarmMission store had created the
mission record. For native-swarm missions, the dispatch is async
(void-promise), so the GET /api/conductor-spawn?missionId=... call
could arrive before the swarm mission was stored, returning 404.
With retry exhausted, the query stopped polling and the mission
remained stuck in the 'running' phase forever.
Changed to retry: Infinity with exponential backoff
(2s, 4s, 8s capped at 10s) so the query keeps polling until the
swarm mission is available.
* fix(conductor): mobile rendering — double header, missing tab bar, CONDUCT truncation, bottom padding
* fix(conductor): remove md:justify-center on home page to prevent content clip
The home page Conductor view used md:justify-center on the main container,
which vertically centers flex content in the available space. When content
height exceeds viewport height, flex centering pushes the top off-screen
(y=-51px), clipping the Conductor badge and header.
Fix: remove md:justify-center so content starts at justify-start (y=24px),
keeping the badge and header visible. Only the home phase had this class;
preview, active, and complete phases were already justify-start.
* fix(conductor-gateway): start session key resolver when prefix is present even without initial sessionKey
When the dashboard returns a sessionKeyPrefix without a sessionKey
(session hasn't resolved yet), the async session key resolver at
line 1581 was gated on both . Since
orchestratorKey was null in this case, the resolver never started
and the session key was never resolved.
Fix: remove the orchestratorKey check — the resolver only needs the
prefix to start polling for a matching session.
* fix(dashboard): prevent OpsStrip text overlap & extend marquee mask at mobile widths
- OpsStrip gateway block: add flex-wrap so '0 active runs' and
'pulse 4h ago' wrap properly on narrow viewports instead of
colliding horizontally.
- AttentionMarquee mask: extend fade zone from 92% to 96% so
marquee items aren't prematurely masked on 390px viewport.
* fix(dashboard): prevent action button overflow at mobile viewport
Change action bar from justify-end to justify-start on mobile
so that NEW CHAT / TERMINAL / SKILLS buttons don't clip the
right edge of the 390px viewport. Desktop remains justify-end.
* fix(files): wrap code viewer text on mobile viewport
The element in the code viewer had default
which caused line overflow on narrow screens. Added so long code comments wrap at 390px viewport instead of
clipping off-screen.
* fix: set tabbar height default to 80px in styles.css, fixing bottom content clipping across all pages
styles.css hardcoded --tabbar-h: 0px, overriding the 80px fallback
in var(--tabbar-h, 80px) used by most pages. This caused bottom
content to be hidden behind the fixed mobile tab bar (~80px tall)
on every non-chat page. The MobileTabBar JavaScript does dynamically
measure and set this variable, but the CSS default was wrong.
* fix(dashboard): shrink New Chat button at smallest mobile viewport
iPhone SE (375px) couldn't fit NEW CHAT + TERMINAL + SKILLS
on one row because the primary button used desktop-sized padding
and text. Reduced New Chat to px-3 py-1.5 text-xs on mobile
(default), restored to px-3.5 py-2 text-sm at sm: breakpoint.
Also removed redundant justify-start (flex-start is the default)
to keep flex-wrap clean.
* fix(swarm): pass prompt via stdin instead of CLI arg to fix dispatch failure
dispatchSwarmAssignments ran hermes chat -q <prompt> with the
full prompt as a command-line argument. For long prompts this
exceeds ARG_MAX or contains unescaped chars, causing execFile
to fail silently ("Command failed"). The worker was marked
dispatched but never actually started, leaving it stuck in
'idle' with 0/1 tasks complete forever.
Fix: pass the prompt through execFile's stdin ('input' option)
instead of as a positional arg. hermes chat -q reads from stdin
when no query argument follows the flag.
---------
Co-authored-by: Waylon Kenning <waylonkenning@Waylons-MacBook-Pro.local>
Co-authored-by: Aurora <myaurora.agi@gmail.com>
Addresses #514 by documenting the env vars needed for LAN/Tailscale
deployments (HERMES_PASSWORD, COOKIE_SECURE, API_SERVER_KEY,
GATEWAY_ALLOW_ALL_USERS) and providing a docker-compose.override.yml
example for publishing ports without loopback binding.
Added troubleshooting table for common Docker startup errors.
Co-authored-by: Aurora <myaurora.agi@gmail.com>
When HERMES_DASHBOARD_URL is set, profile list/read now fetches from
the dashboard /api/profiles endpoint before falling back to local
filesystem reads. This fixes empty profile lists in split-host Docker
deployments where the workspace container has no local profiles
directory but the agent host's dashboard already exposes profile data.
- Add listProfilesWithFallback() and readProfileWithFallback()
- Wire route handlers to use the new async fallback functions
- Keep sync filesystem code unchanged for colocated deployments
- Dashboard calls use existing HERMES_API_TOKEN/CLAUDE_API_TOKEN auth
Closes#499
Co-authored-by: Aurora <myaurora.agi@gmail.com>
* feat: add Nix flake and module for hermes-workspace deployment
* chore: modernize Nix build by migrating to generic nodejs/pnpm packages and adding direnv integration
Resolve the Hermes CLI path explicitly before running profile cron
create/update/action commands so Workspace does not depend on the
launch environment PATH.
This fixes job edit/action failures that surfaced as:
spawnSync hermes ENOENT
Also refresh the tracked Electron server bundle so the desktop app
picks up the server-side fix.
* chore: preserve pnpm approved builds
* fix(router): support runtime basepath override for reverse-proxy hosting
The TanStack router was created without a `basepath` option, so the same
built bundle could not be hosted under a path prefix (e.g. behind a
reverse proxy that mounts the app at `/workspaces/<id>/`). Hard refreshes
appeared to work because SSR runs at the proxy-stripped path, but
client-side navigation to dynamic routes such as `/chat/$sessionKey`
silently fell through to the catch-all `/$` route — rendering the
"404 — Not Found" page from inside the SPA.
Read an optional `window.__HERMES_WORKSPACE_BASEPATH__` global and pass
it through to `createRouter`. When unset, behavior is unchanged
(`basepath: '/'`). The value is normalized so callers can pass either
`/workspaces/abc`, `workspaces/abc`, or `/workspaces/abc/` without
upsetting TanStack's pathname matching.
This lets hosting layers inject a tiny inline script before the bundle
loads to mount the app at any path, without rebuilding.
* fix(chat): prevent stale thinking state after page refresh (closes#449)
Root cause: sessionStorage 'waiting' flags persisted across page refreshes
even for completed conversations. The Zustand store restored these stale
entries on mount, and the active-run API check cleared them async —
but there was a visible render window where the UI showed 'thinking'.
Fix:
1. Added activeRunCheckDone state that gates the waitingForResponse memo.
While the active-run API check is pending, stale restored state is
not trusted — the thinking indicator stays hidden until verification.
2. Added onCheckComplete callback to useActiveRunCheck hook that fires
after the API check finishes (success or error), unblocking the gate.
3. Added a useEffect that detects restored stale waiting state and sets
pendingVerifySessionKeyRef so the gate only applies to the key that
needs verification — not to genuine active streams.
Test: e2e/chat-thinking-state.spec.ts injects a stale sessionStorage
entry before page load, then verifies no thinking indicator appears
and the stale entry is cleaned up by the API check.
* fix(chat): eliminate duplicate messages flicker on stream completion (closes#441)
Root cause: onDone handler used queryClient.invalidateQueries() which
triggers an async refetch. During the refetch window, mergeHistoryMessages
ran with stale cache data + realtime buffer, producing visible duplicates
(extra user message + blank line) for 1-2 seconds until refetch completed.
Fix: Directly merge realtime buffer into history cache via setQueryData(),
then clear buffer synchronously. Background refetch runs after for
consistency but doesn't block rendering.
* fix: restore hermes-config and config-patch API routes
The Aurora rename migration (efcb7d14) renamed hermes-config.ts to
claude-config.ts, but the frontend and routeTree.gen.ts still reference
the original paths. This caused all /api/hermes-config and /api/config-patch
requests to fall through to the SPA HTML fallback, breaking config saves
from the settings dialog and provider wizard with 'Failed to save' errors.
Restored by creating thin route files that delegate to the existing
handleHermesConfigGet/handleHermesConfigPatch handlers from
src/server/hermes-config-route.ts.
Fixes the settings dialog (hermes-config GET/PATCH) and provider wizard
(config-patch POST) config save flows.
* fix(server): add essential env vars to terminal session
* fix(swarm): worker card shows stale state after task completes
deriveWorkerState derived the badge from currentTask title substring
matching and markCheckpointResult never cleared currentTask on terminal
checkpoints, so a finished worker's card stayed permanently 'working'.
- swarm-dispatch.ts: clear currentTask on terminal checkpoint
(checkpointStatus !== 'in_progress'), matching conductor-stop's reset
- operational-worker-card.tsx: deriveWorkerState reads authoritative
checkpointStatus/state first, title heuristic only while in_progress
- swarm2-screen.tsx: pass checkpointStatus/state into the card
* fix(portable-history): replay authenticated portable chat history
* fix(config): keep legacy claude-config shim on shared handlers
* fix: harden splash hydration and docker uid mapping
* fix: keep seen update notes dismissed
* feat: consolidate workspace state under configurable state directory (closes#439)
Adds HERMES_WORKSPACE_STATE_DIR env var support, consolidating 5
scattered state files under a single configurable directory.
Changes:
- New src/server/workspace-state-dir.ts with getStateDir() utility
honoring HERMES_WORKSPACE_STATE_DIR → HERMES_HOME/workspace →
CLAUDE_HOME/workspace → ~/.hermes/workspace (fallback chain)
- Updated gateway-capabilities.ts (workspace-overrides.json)
- Updated mcp-presets-store.ts (mcp-presets.json)
- Updated mcp-hub-sources-store.ts (mcp-hub-sources.json)
- Updated mcp-tools-cache.ts (cache/mcp-tools.json)
- Updated knowledge-config.ts (knowledge-config.json)
- Removed 5 duplicated hermesHome() functions, replaced with shared
getStateDir() import
Test: 6 vitest unit tests covering all env var priority combinations
(cherry picked from commit d6bebe0614b0c7b9015bac5e35d315a8450ac146)
* fix(conductor): surface native-swarm progress and harden worker startup
* feat(chat): safely render HTML message markup
* fix(chat): surface installed skills in slash autocomplete
* fix: add swarm runtime reset endpoint
* fix(conductor): mobile rendering — add overflow-y-auto, mobile bottom padding, OfficeView responsive height, tabbar fix
* fix(send-stream): preserve runs on client disconnect
* fix(profiles): skip profiles/default duplicate card
* fix: accept HERMES_AGENT_PATH override
* fix(profiles): allow disabling sticky active_profile writes
* fix: preserve workspace chat session routing
* fix(portable-history): skip replay when gateway session continuity is available
---------
Co-authored-by: Hermes Agent <hermes-agent@local.invalid>
Co-authored-by: jack <jack@hijak.dev>
Co-authored-by: Waylon Kenning <waylonkenning@Waylons-MacBook-Pro.local>
Co-authored-by: Michael Rodriguez <michael@rivercity-industries.com>
Co-authored-by: Vu Tran <baysao@gmail.com>
Co-authored-by: iltaek <iltaekkwon@gmail.com>
Co-authored-by: Aurora release bot <release@outsourc-e.com>
Co-authored-by: jonathanmalkin <jonathan.d.malkin@gmail.com>
Co-authored-by: KT-Hermes <ktadmin@kt-bot2.tekeis.net>
Merging the playground performance pass after rebasing it onto current main and re-running a fresh local production build. The branch stays scoped to HermesWorld performance and asset-weight reductions.
- make install.sh resilient when pnpm is only available via corepack
- cap pnpm build heap for low-memory installs
- relabel Workspace Kanban sidebar entry to Tasks and clarify copy
Co-authored-by: Aurora release bot <release@outsourc-e.com>
* fix(start): use server-entry wrapper for production
* fix(swarm): reconcile oneshot checkpoints and ignore phantom blockers
* fix(profiles): sync editable descriptions from profile config
* fix: show cron jobs across Hermes profiles
* fix(capabilities): clarify dashboard-backed API detection
* feat: make Conductor use native Swarm fallback
Treat Workspace-native Swarm as the official Conductor fallback when the dashboard mission API is unavailable. Preserve dashboard-first dispatch, native status/cancel handling, provider-neutral setup docs, and regression coverage for gateway capability detection, swarm health, roster/profile handling, and native Conductor responses.
* fix(usage-meter): reposition menu trigger for better alignment
- Adjusted the position of the menu trigger in the usage meter component to enhance layout consistency and user experience.
fix(chat-panel): adjust position of chat panel toggle button
- Updated the positioning of the chat panel toggle button to improve visibility and accessibility by changing its bottom and right offsets.
* fix(stt): wire Groq/OpenAI voice transcription into chat
* Fix Workspace Kanban loopback dashboard link
* fix(update): do not open historical release notes on startup
* fix(chat): clear stale thinking runs reliably
* fix(dashboard): trust sessions endpoint for status
* fix(settings): address review — local default, OAuth lifecycle, validation
* fix(dashboard): always scrape live session token from HTML
* fix(chat): avoid portable history replay on bound sessions
* fix(settings): remove dead smart routing controls
* fix(tasks-api): guard against HTML catch-all in probeBackend
The /api/hermes-tasks route was renamed to /api/claude-tasks in commit
efcb7d14, but the probe logic still listed the old route as a candidate.
When probed, the SPA catch-all returned a 200 HTML response instead of
a 404, so probeBackend() treated it as a valid (empty) backend and then
failed when the actual task fetch threw.
Fixes:
- probeBackend() now checks Content-Type: application/json and returns
-1 for non-JSON responses, so future route renames degrade gracefully.
- resolveBackend() now only selects hermes if hermesCount > 0, defaulting
to claude-tasks (the active backend post rename) when hermes is absent.
* fix(terminal): default cwd to ~ and fallback if path missing
PTY helper chdir fails when ~/.hermes is absent (common in Docker).
Default shell cwd to home; server falls back to HOME if cwd does not exist.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(settings): satisfy lint on custom providers UI
* fix(docker): add -m flag to useradd so workspace home dir is created
Without -m, the system account has no /home/workspace directory.
The auth middleware tries to write the session store at
/home/workspace/.hermes/workspace-sessions.json; mkdirSync fails with
EACCES because /home/ is root-owned (755), causing the 'Failed to
persist session store' warning and a 500 on every authenticated route.
Adding -m causes useradd to create and chown /home/workspace correctly
so the session store can be written on first login.
* fix(tasks): preserve real session links and restore task launch flow
* fix(launchd): install macOS plist from server-entry template
* fix(docker): expose dashboard API and persist workspace volumes
* fix(jobs): serialize deliver targets for cron API
* Make Hermes Workspace installable as PWA
* chore(deps): pin direct tanstack versions
* feat: align semantic Hermes swarm agents
Add semantic swarm roster metadata, profile/tool/skill docs, shared semantic worker ID validation, focused roster regression coverage, and one-shot checkpoint capture for dispatch smoke tests.
* fix(conductor): pass through sessionKeyPrefix from portable spawn result
sessionKeyPrefix was hardcoded to null in conductor-spawn.ts, breaking
async session resolution when the dashboard backend returns a prefix.
Now mirrors the sessionKey pattern and passes through the value from
the spawn result.
Co-authored-by: Hermes Agent
* feat(swarm): bridge workspace kanban to native Hermes
* fix(chat): keep portable main pinned without breaking resolved sessions
* Add Windows startup script for Hermes Workspace
Document PowerShell usage for launching and restarting gateway + workspace via WSL tmux.
Co-Authored-By: Oz <oz-agent@warp.dev>
* fix(config): name Hermes Agent in restart notice
* fix(swarm): reconcile aggregate semantic worker exports
---------
Co-authored-by: Aurora release bot <release@outsourc-e.com>
Co-authored-by: motoki takahashi <motokitakahashi@motokinoMac-mini.local>
Co-authored-by: Vicky Wonder <vicky@openclaw.ai>
Co-authored-by: Vitaliy Isikov <visikov+supagoku@gmail.com>
Co-authored-by: Hermes Agent <hermes-agent@local>
Co-authored-by: Nikolay Mohr <nikomohr96@gmail.com>
Co-authored-by: Niko Mohr <niko@friendsfromcollege.de>
Co-authored-by: wtchronos <262830926+wtchronos@users.noreply.github.com>
Co-authored-by: Dak0verflow <dakotaferris@gmail.com>
Co-authored-by: norema <mamadou.marone.19@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: daoyuan <ludaoyuan1989@gmail.com>
Co-authored-by: firemountain <firemountain@gmail.com>
Co-authored-by: RAZSOC Local <razsoc@local>
Co-authored-by: Waylon Kenning <waylonkenning@Waylons-MacBook-Pro.local>
Co-authored-by: Kublai <kublai@kublai.local>
Co-authored-by: justa <justa@local>
Co-authored-by: Oz <oz-agent@warp.dev>
- Remove the MseeP.ai security badge - the crocodile mascot doesn't fit
Workspace branding and Workspace isn't an MCP server (it's an agent UI),
so the audit badge was confusing for users skimming the readme.
- Bump the visible version badge from 2.1.3 to 2.3.0 to match the actual
shipped version.
- Add a dedicated 'Pair an Agent with the Workspace' section between the
install paths and Docker, covering:
- Architecture diagram (which service does what on which port)
- Three-command pairing workflow
- Verify-pairing curl checks
- .env reference table
- Common pairing scenarios (local, Tailscale/VPN, multi-profile, remote)
- Live re-pairing without restart
- Troubleshooting for the four most common pairing errors
The previous readme split this information across the 'Already running
hermes-agent? Attach the workspace to it' and 'Manual install' sections,
which was hard to follow if your question was just 'how do I connect
agent X to workspace Y'.
Co-authored-by: Aurora release bot <release@outsourc-e.com>
* Upload SciFi theme screenshot
* fix: stabilize workspace swarm process spawning (#302)
Co-authored-by: Jarno de Vries <jarno@match-day.nl>
* feat(theme): add SciFi theme (dark + light variants) (#303)
* feat(theme): add SciFi theme (dark + light variants)
- Add scifi-theme.css with neon accents, glow effects, gradient backgrounds
- Register SciFi in theme.ts with dark/light color schemes
- Add SciFi option in settings UI
- Import scifi-theme.css in styles.css
* docs: add SciFi theme screenshot
---------
Co-authored-by: Fungraphique <fungraphique@jarvis.local>
* fix(chat): cross-session response contamination + /new opens previous chat (#297, #300)
Two related session-routing bugs that landed responses (or new-chat
clicks) into the wrong session.
#297 — cross-session response contamination
When the user navigated to a new chat while a previous chat was still
streaming, the previous chat's response chunks would land in the
**new** chat. Three fixes:
* useStreamingMessage now bumps a streamGenerationRef on every
startStreaming call. The fetch-reader loop captures that token at
start and re-checks it on every reader.read() and between events
in the same batch. If the token has changed (because the user
started a different stream), the loop cancels the reader and exits
without dispatching anything. This closes the brief race between
abortController.abort() and the underlying fetch reader actually
stopping, during which buffered chunks were silently writing into
activeSessionKeyRef.current (which had already been switched to
the new session).
* chat-screen now cancels the in-flight stream on session-key change
via a useEffect keyed on (activeCanonicalKey, activeFriendlyId,
isNewChat). Previously nothing cancelled the stream on navigation
\u2014 only the user clicking the explicit Stop button (handleStop)
called cancelStreaming().
#300 — /new slash command opens last chat instead of new session
/new was calling navigate({ to: '/chat' }), but the /chat index route
unconditionally redirects to localStorage('claude-last-session'), so
/new always landed in whichever chat was last active. Fixed at three
entry points so all 'new chat' actions go through the explicit 'new'
sentinel:
* /new in chat-screen.handleUiSlashCommand
* /new in command-palette.runSlashCommand
* 'New Chat' quick-action tile in the search modal
The 'Chat' nav link in the sidebar still goes to /chat (= last session)
\u2014 that's the correct behaviour for a screen-level nav target. Only
'new' actions are routed to the new sentinel.
Closes#297, #300
* fix(terminal): keep PTY alive across SSE disconnects + auto-reattach (#298)
The browser terminal periodically 'reset back to prompt' during normal
use because any transient SSE disconnect (network blip, browser tab
suspension, HMR reload, dev-server restart) tore down the user's PTY
and dropped them into a fresh shell.
Root cause: terminal-stream's request.signal abort handler called
session.close(), which SIGTERM'd the underlying Python PTY helper.
There was also no auto-reconnect on the client \u2014 a single dropped
read terminated the loop, called /api/terminal-close, cleared the
tab's sessionId, and left the user with an idle tab.
Fix in three parts:
1) terminal-sessions: TerminalSession gains markDetached() and
markAttached(). markDetached() starts a TTL timer (default 5 min,
override via HERMES_TERMINAL_DETACH_TTL_MS) that reaps the PTY only
if no client reattaches in time. The map keeps the session live in
the meantime.
2) terminal-stream: accepts an optional sessionId in the POST body. If
the id matches a still-alive session, the route reattaches to it
instead of spawning a fresh PTY. The 'session' event payload now
includes a 'reattach' flag. On SSE abort, we just detach listeners
and call session.markDetached() \u2014 the PTY stays running.
3) terminal-workspace: passes sessionId on every connect, so reconnect
reattaches automatically. When the read loop ends and the tab still
has a sessionId, we attempt a single quick reattach with a
'[reconnecting...]' nudge to the user instead of tearing the tab
down. /api/terminal-close is no longer called on stream end \u2014 the
server-side TTL handles abandoned sessions.
Fixes#298
* fix(scifi-theme): remap amber tokens to cyan/teal — no more pale yellow in SciFi
Amber colors (used for warnings/alerts in usage meter, agent thinking,
inspector badges, etc.) were not remapped in the SciFi theme, causing:
- Pale yellow bg-amber-100 with light text → unreadable menu items
- Jarring yellow accents breaking the cyberpunk aesthetic
- Trigger pill and selection states with impossible contrast
Remap all --color-amber-* tokens:
- Dark theme: amber → cyan/teal gradient (#041418 → #ccfaff)
- Light theme: amber → teal gradient (#e8f4f6 → #0a1628)
This replaces the previous .bg-amber-100 !important hack which broke
the usage meter bar by forcing dark text on already-dark remapped
primary-50 backgrounds.
* fix(scifi-theme): tweak amber remap contrast values
Brighten the amber→cyan tokens slightly so bg-amber-100 is visible
on --theme-panel (#0d1b2a) and text colors have good contrast
on the darker backgrounds.
* fix(theme): SciFi — visible usage pill + selected menu item colors
- Brightened amber-100 to #0c3245 (was #0a2633, too dark on #060b18 bg)
- Brightened amber-50/200/300/400/500/600/700 gradient for better contrast
- Added !important overrides for .bg-amber-100 to force background and
text color, overriding MenuItem inline styles that were blocking
the selected menu item appearance in SciFi dark theme
* fix(scifi-theme): add yellow, neutral, and white overrides for dark mode
- Add --color-yellow-* remap to teal-warm tones (was missing entirely)
- Add --color-neutral-* remap to dark slate/navy tones
- Add .bg-white override to theme-card for dialog backgrounds
- Add .bg-amber-100 color override for MenuItem selected state
(MenuItem uses inline style color:var(--theme-text) which overwrites
Tailwind text-amber-800, now overridden with !important)
- Add scifi-light remaps for yellow, red, emerald, neutral tokens
- Fixes: pale yellow/white backgrounds in usage meter menu, View Details
dialog, status badges, progress bars, and tab pills in SciFi dark mode
* fix: replace bg-white with bg-primary-50 in usage-details-modal for theme compatibility
- All bg-white/bg-white/N in usage-details-modal replaced with bg-primary-50/N
which is properly remapped by the SciFi theme (--theme-card/panel)
- Active tab pill: bg-white → bg-primary-100, text-primary-900 → text-primary-800
- Set as Default button: bg-white → bg-primary-50, hover:bg-primary-50 → hover:bg-primary-100
- Removed aggressive bg-white !important overrides from scifi-theme.css that
broke borders, switches, and other elements using white elsewhere
* fix(scifi-theme): rewrite theme following nous pattern
- Replace hex values with var(--theme-*) references (same as nous theme)
- Remove broken red/emerald/yellow/neutral remaps that caused border/bg issues
- Add !important on dark-mode primary/accent remaps (needed for Tailwind v4 oklch)
- Move @import scifi-theme.css to END of styles.css (after .system dark rules)
- Keep essential .bg-amber-100 overrides for menu selected state
- Result: 238 lines (was 350+), clean structure matching nous theme pattern
Root cause: Tailwind v4 uses oklch color-mix internally for utility classes.
Simple --color-amber-100: #0f3547 overrides don't work because .border-primary-200
resolves via color-mix, not via the CSS custom property. Using var(--theme-*)
with !important ensures proper resolution.
* fix(scifi-theme): add !important to all dark mode token remaps
Tailwind v4 defines color tokens as oklch in @layer theme, which has
higher specificity than plain [data-theme] selectors. Without !important,
the remaps were ignored and components displayed native Tailwind colors
(white, amber, neutral-gray) instead of the SciFi palette.
Also remap --color-white to #0d1b2a so bg-white becomes dark navy
in SciFi dark mode (fixes chat-controls popover white background).
* feat(scifi): active tab indicator — cyan accent glow on Session/Providers tabs
* fix(scifi-theme): review fixes - 9 corrections
* fix: restore tab selector without role=tablist (component doesn't use it)
* fix(scifi): tab selector uses .bg-primary-50 > button instead of [role=tablist]
The usage-details-modal tabs don't use role=tablist ARIA attribute,
so [role=tablist] selector never matched. The tab container has
bg-primary-50 and direct child buttons with bg-primary-100 when active.
---------
Co-authored-by: jarnodevries-byte <jarnodevries@gmail.com>
Co-authored-by: Jarno de Vries <jarno@match-day.nl>
Co-authored-by: Fungraphique <fungraphique@jarvis.local>
Co-authored-by: Aurora release bot <release@outsourc-e.com>
* Hide normal chat sessions from agent sidebar
* Fix context meter for portable chat sessions
* Persist local session renames
* Show local session titles in sidebar
* Harden Workspace asset serving and expose Kanban nav
---------
Co-authored-by: clawbot <clawbot@clawbots-Mac-mini.local>
The /api/update/status poll returns the same pendingReleaseNotes on every
poll. Workspace was running storeNotes() inside a useEffect keyed on those
notes, and storeNotes unconditionally cleared NOTES_SEEN_KEY. So every
time the user refreshed the page (or a poll fired), the seen marker was
dropped and the modal popped again.
Now we only clear the seen marker when the notes ID has actually changed
(i.e. a new release with different content). Identical payloads on
refresh keep the seen marker intact.
Closes#356
Co-authored-by: Aurora release bot <release@outsourc-e.com>
The hermes-agent image's default entrypoint is the interactive CLI which
exits immediately under `docker compose up -d`, causing the gateway to
appear absent and the Workspace healthcheck/connection to fail with
"Could not reach Hermes gateway". Override the command to `gateway run`
so the long-running API/health server starts.
Reproducible without this fix: a fresh `docker compose up -d` against
upstream main fails with the symptoms reported in #360 across several
users (different OSes), and the only working remedy was for users to
manually patch their compose with `command: gateway run`.
Closes#360
Co-authored-by: Aurora release bot <release@outsourc-e.com>
When PR #311 unified the Tasks board to use the Kanban backend
(/api/claude-tasks), installations that store tasks in the flat-file
store at ~/.hermes/tasks.json (served by /api/hermes-tasks) silently
lost all task visibility — the board rendered empty with no error.
Root cause: tasks-api.ts hardcoded BASE = '/api/claude-tasks', which
routes through the Kanban abstraction layer. The hermes-tasks endpoint
(the canonical store agents and cron jobs write to) was orphaned.
Fix: replace the hardcoded BASE with an automatic backend resolver that
probes both endpoints in parallel on first page load, picks whichever
has data, and caches the result for the session.
Selection logic:
- hermes-tasks wins if it has >= claude-tasks task count (preferred
as it is the store agents/cron write to)
- claude-tasks wins only if it has data and hermes-tasks is empty
- if both are empty, hermes-tasks is the default (correct for new installs)
All mutations (create, update, move, delete, launch) use the same
resolved backend so reads and writes are always consistent.
Assignees endpoint is also resolved to match (hermes-tasks-assignees
vs claude-tasks-assignees) so profile lookups work against the right
data store.
Exports getActiveBackend() and resetBackendResolution() so callers
can inspect or force a fresh probe (e.g. after backend config change).
Tested against: hermes-tasks (80 tasks), claude-tasks (0 tasks) →
board correctly shows 80 tasks after auto-selecting hermes backend.
The context usage calculator was defaulting kimi-k2.6 to 200k tokens,
causing incorrect 100% context pressure alerts on new chats.
- Add 'kimi-k2.6': 256_000 to MODEL_CONTEXT_WINDOWS map
- Fixes false-positive context window warnings for kimi-k2.6 users
Refs: context-usage.ts hardcoded model list
Co-authored-by: Dev <dev@example.com>
Installs that were set up before the claude→hermes rename may have their
git remotes named claude-workspace and claude-agent rather than the new
hermes-workspace / hermes-agent names. Without these aliases the update
checker misidentifies the remote, reports no update URL match, and the
Update Center shows a misleading error.
Adds backward-compatible aliases so both old and new remote naming
conventions are recognised.
Co-authored-by: admin <admin@fattony.local>