fix: batch remaining workspace bugfix slices (#483)

* 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>
This commit is contained in:
Eric
2026-05-19 16:27:10 -04:00
committed by GitHub
parent e1470084d2
commit 4f75b5835c
62 changed files with 1937 additions and 569 deletions

View File

@@ -19,16 +19,18 @@ import viteTsConfigPaths from 'vite-tsconfig-paths'
// ---------------------------------------------------------------------------
/** Resolve the hermes-agent directory using a priority-ordered fallback chain:
* 1. CLAUDE_AGENT_PATH env var (explicit override)
* 2. ../hermes-agent — sibling clone (standard README setup)
* 3. ../../hermes-agent — one level up (monorepo / nested workspace)
* 1. HERMES_AGENT_PATH env var (explicit override)
* 2. CLAUDE_AGENT_PATH env var (legacy override)
* 3. ../hermes-agent sibling clone (standard README setup)
* 4. ../../hermes-agent — one level up (monorepo / nested workspace)
* Returns null if none found.
*/
function resolveClaudeAgentDir(env: Record<string, string>): string | null {
const candidates: string[] = []
if (env.CLAUDE_AGENT_PATH?.trim()) {
candidates.push(env.CLAUDE_AGENT_PATH.trim())
const explicitAgentPath = env.HERMES_AGENT_PATH?.trim() || env.CLAUDE_AGENT_PATH?.trim()
if (explicitAgentPath) {
candidates.push(explicitAgentPath)
}
// Resolve relative to the workspace root (parent of hermes-workspace/)
@@ -155,7 +157,7 @@ const config = defineConfig(({ mode, command }) => {
'[hermes-agent] Could not find hermes-agent installation.\n' +
' Run the installer:\n' +
' curl -fsSL https://hermes-workspace.com/install.sh | bash\n' +
' Or set CLAUDE_AGENT_PATH in .env to point at your hermes-agent clone.',
' Or set HERMES_AGENT_PATH (or legacy CLAUDE_AGENT_PATH) in .env to point at your hermes-agent clone.',
)
return
}