Running log of development sessions. Most recent at top.
Conductor V2 — Gateway Port (v1.20.0)
Replaced the stub v1.19.0 Conductor with a faithful adapted port of the upstream gateway conductor. The old file-backed mission-store approach was entirely removed and replaced with gateway-native integration.
Key components built:
use-conductor-gateway.ts— 1386-line hook managing mission lifecycle, session polling, worker tracking, localStorage persistenceoffice-view.tsx— 957-line animated SVG office with 3 layouts (Grid, Roundtable, War Room), desk/monitor SVGs, agent wandering, speech bubblesagent-avatar.tsx— 10 pixel-art robot variants with accent colorsconductor-spawn.ts/conductor-stop.ts— Server routes for Hermes cron job creation and session termination- Phase components:
conductor-home.tsx,conductor-active.tsx,conductor-complete.tsx conductor-settings.tsx— Model selectors, projects dir, max parallel, supervised togglecost-tracker.tsx/mission-event-log.tsx— Token cost display and cycling status indicators
Infrastructure changes:
- Deleted: mission-store.ts, 5 API routes, missions-api.ts, 2 test files, 7 old UI components
- Updated: operations-aggregator now queries live gateway sessions (async)
- Added: CSS office animation keyframes (glow pulses, idle float)
- Tests: 190 passing (was 199 — deleted 13 old tests, added 4 new)
Conductor, Operations & Tasks — v1.19.0 (20 implementation tasks)
Ported the Conductor, Operations, and Tasks features from upstream hermes-workspace using an "adapted port" approach: upstream data models and API contracts preserved, but stores rewritten with file-backed persistence (matching existing crew-store.ts pattern), UI rebuilt with the Hermes Studio design system (var(--theme-*) CSS variables, DS components, HugeIcons).
Types & Templates (Tasks 1–2)
src/types/task.ts— HermesTask, TaskColumn, TaskPriority, TaskSourceType, column constantssrc/types/conductor.ts— Mission, MissionWorker, MissionEvent, ConductorPhase, MissionStatus, WorkerStatussrc/types/operation.ts— OperationAgent, OperationAgentStatussrc/types/template.ts— Extended withtemplateType: 'crew' | 'conductor',ConductorTemplateConfig, 4 built-in conductor templates added to template-store
Server Stores (Tasks 3–5)
src/server/task-store.ts— File-backed to.runtime/tasks.json; CRUD + move + filter; publishChatEvent on mutationssrc/server/mission-store.ts— File-backed to.runtime/missions.json+.runtime/mission-events.json; full lifecycle with workers and events; publishChatEvent on mutationssrc/server/operations-aggregator.ts— Read-only aggregator querying crew-store and mission-store
API Routes (Tasks 6–8)
- 8 new route files: tasks CRUD + move, missions CRUD + abort + events, operations overview
- Client API helpers:
tasks-api.ts,missions-api.ts,operations-api.ts
UI Screens (Tasks 9–16)
- Tasks screen: 4 files — Kanban board with 5 columns, HTML5 drag-and-drop, task dialog, priority badges
- Conductor screen: 9 files — Phase state machine (home→preview→active→complete), worker cards, event log, cost tracker
- Operations screen: 5 files — Agent grid/outputs toggle, status filter, crew detail Operations tab
Integration & Polish (Tasks 17–20)
- Sidebar: Conductor, Operations, Tasks nav items added
- Cross-links: conductor missions auto-create tasks on Kanban board
- Audit trail: all store mutations emit events via publishChatEvent
- Workspace shell: mobile page titles for new routes
- Templates gallery: conductor category icon/label added
Bug fixes during implementation:
conductor-screen.tsx:100—CrewTemplate | nullpassed whereCrewTemplateexpected; fixed MissionPreview propstask-card.tsx:34—styleprop on DSCard(doesn't accept it); moved to wrapping<div>templates-gallery.tsx:30— Missing conductor entry in category map; added- Test failures from
publishChatEventrequiring SQLite; addedvi.mockfor chat-event-bus in test files
Test results: 199 tests across 17 files, all passing. Zero regressions.
Key technical decisions:
- Unified template system (crew + conductor share one
CrewTemplatetype withtemplateTypediscriminator) instead of parallel template stores - Operations aggregator is read-only (no separate store), combining crew-store + mission-store queries
- Cross-linking via
sourceType/sourceIdfields on tasks — conductor missions auto-create tasks, UI links back to conductor - File-backed stores with in-memory cache + deferred disk writes (same pattern as crew-store.ts)
- All new screens use 3-second react-query polling for live updates
Feature sprint completion — v1.18.0 (Tasks #13–#20)
All 8 roadmap tasks from the backlog built in this session and the previous one. Zero TypeScript errors, 59/59 tests passing throughout.
Task #13 — Command Palette (Ctrl+K)
use-global-shortcutshook dispatcheshermes:toggle-sidebarcustom event onCtrl+K/Cmd+K; no changes needed to the existingCommandPalettecomponent
Task #14 — System Health Panel
<SystemMetricsFooter />pollsGET /api/system-healthevery 10 s- Fixed bottom bar: CPU%, memory, disk, uptime; green <60%, amber 60–80%, red >80%
- Conditional render in
WorkspaceShellbehindsettings.showSystemMetricsFooter
Task #15 — Token Usage Time-Series Chart
- 14-day
AreaChart(recharts) added tousage-details-modal.tsx buildDayBuckets()pre-fills 14 days, groups sessions by timestamp- recharts
Cellnot available in bundler mode — workaround: singlefillprop onBar
Task #16 — Event Store Analytics
getAnalytics()added toevent-store.tsusing SQL (json_extract,GROUP BY,strftime) — never loads raw payloads into JSGET /api/state-analyticsroute +AnalyticsScreenat/analytics- 4 stat cards, 14-day stacked bar chart, horizontal top-15 tool chart
Task #17 — Identity File Editor
- New
'identity'section in Settings with tabbed editor forSOUL.md,persona.md,CLAUDE.md - Reads via
GET /api/files?action=read, writes viaPOST /api/files
Task #18 — Patterns & Corrections Viewer
PatternsCorrectionScreenat/patterns- Parser splits
MEMORY.mdon§delimiter; classifies byCORRECTION:prefix - Two tabs: patterns (read-only cards) and corrections (cards + add form + delete)
Task #19 — Session History Archive
SessionHistoryScreenat/session-history- Two-pane: sortable session list (date/model/msgs/tokens/cost) + lazy message thread
- Aggregate stats bar; message bubbles for user/assistant
Task #20 — Systemd Auto-start
scripts/hermes-studio.serviceunit template withHERMES_INSTALL_DIRplaceholderscripts/install-systemd.shCLI: install/uninstall/start/stop/enable/disable/statusGET /api/systemd-status+POST /api/systemd-controlAPI routes- Settings → Auto-start UI: status indicators, action buttons,
systemctl statusoutput panel - Graceful degradation on non-Linux platforms
Docs + release
- CHANGELOG.md:
[Unreleased]promoted to[1.18.0]with full entry - README.md: version badge bumped to 1.18.0; 8 new feature bullets; roadmap table updated (all 8 tasks → ✅ Shipped v1.18.0)
package.jsonversion bumped to1.18.0
Key technical notes:
routeTree.gen.tsis@ts-nocheck; onlyFileRoutesByPathinsidedeclare module '@tanstack/react-router'matters for compile-time checking — must add entries there for every new route- recharts
Cellnot importable undermoduleResolution: bundlerdespite being in type definitions BrainIconwas already imported inchat-sidebar.tsx(agent personas) — reused, not duplicated- MEMORY.md uses
§as entry separator (not##headings)
Roadmap audit + task backlog creation
Cross-referenced the README roadmap table against the actual codebase to determine true status of every listed item. Found 5 features marked as 🔜 Planned that were already shipped, and 2 marked as in-progress/coming soon that had no code. Corrected all statuses in the README.
Actual status of roadmap items:
- Audit Trail → ✅ v1.13.0 (was 🔜 Planned)
- Test Suite + CI Badges → ✅ v1.15.0 (was 🔜 Planned)
- Clone Crew → ✅ v1.14.0 (was 🔜 Planned)
- Setup Wizard → ✅ v1.16.0 (was 🔜 Planned)
- Command Palette →
⚠️ Partial — component exists, Ctrl+K binding missing - System Health Panel →
⚠️ Partial — setting + layout reserved, no component - Native Desktop App → ❌ No code (was 🔨 In Development — corrected to Planned)
- Cloud / Hosted → ❌ No code (was Coming Soon — corrected to Planned)
Task backlog created (Tasks #13–#20):
- #13 Wire Ctrl+K keybinding to Command Palette
- #14 Build System Health Panel footer component
- #15 Add Token Usage Time-Series Chart
- #16 Build State.db Analytics screen
- #17 Build Identity File Editor
- #18 Build Patterns & Corrections Viewer
- #19 Build Session History Archive
- #20 Add Systemd Auto-start support
Bugfix sweep + CI infrastructure repair (v1.17.1)
Resolved a GitHub secret scanner alert, cleared all TypeScript errors, and fixed a broken CI pipeline.
Secret scanner:
wx1234567890abcdefin the WeCom settings placeholder matched Tencent WeChat App ID pattern → replaced withyour-corp-id
TypeScript errors (5 fixes, now 0 errors project-wide):
chat-screen.tsx—APPROVAL_APPROVAL_RECEIPT_TTL_MSdouble-prefix typo →APPROVAL_RECEIPT_TTL_MSagent-library-screen.tsx—toast.success()/toast.error()don't exist;toastis a plain function →toast(msg, { type })skills-screen.tsx— TanStack Query narrowsdatatoundefinedwhenisPending, making.sourcefail → cast toHubSearchResponse | undefinedcrew-store.test.ts—profileNamewas added toCrewMemberafter the test fixture was written → addedprofileName: nullchat/$sessionKey.tsx—forcedSession?.friendlyId === xdoesn't narrow type in the truthy branch →forcedSession !== null && forcedSession.friendlyId === x
CI rewrite:
- Project uses pnpm; CI was using
npm installwhich fails withworkspace:protocol errors in pnpm-installed deps - Rewrote
.github/workflows/ci.ymlto usepnpm/action-setup@v4,pnpm install --frozen-lockfile, pnpm-cached Node setup,pnpm exec playwright - Added
@playwright/testto devDependencies (was missing; bothplaywright.config.tsandtests/e2e/smoke.spec.tsimport from it) - Committed
pnpm-lock.yaml(was not tracked; CI now uses--frozen-lockfilefor reproducibility)
Cleanup:
- Removed
feature/ds-consistencyworktree and local + remote branch - Updated stale project memory file (5-day-old snapshot showed features as not-started that were long done)
Result: 59/59 unit tests passing, 0 TS errors, CI pipeline correctly wired end-to-end.
Hermes agent v0.8.0 + v0.9.0 compatibility audit — 12 tasks, all shipped
Researched both release notes, planned 12 UI-surfaceable tasks, implemented and committed.
Hermes v0.9.0 (UI changes):
- Fast Mode button —
FlashIcontoolbar button inchat-composer.tsx; wired to existingfastModestate +effectiveFastModesend logic (the state and/fastslash handler already existed; only the visible button was missing) /fast,/compress,/debugadded to slash-command autocomplete inslash-command-menu.tsx- API_SERVER_KEY — new password field in Settings → Connection (
use-settings.ts+settings-dialog.tsx); for non-loopback Hermes instances - Backup / Import —
triggerBackup()(POST/api/hermes-proxy/api/backup) +handleImport()(POST…/api/backup/import) buttons added to Settings connection section; hidden<input type="file">for import - BlueBubbles, WeChat, WeCom — three entries added to
CHAT_PLATFORMSarray inroutes/settings/index.tsx; reuses existing genericPlatformsSectionrenderer, zero new components GET /api/provider-usage— new Studio route (routes/api/provider-usage.ts) fetches Hermes/api/usage(v0.9.0 rate-limit capture), maps toProviderUsageEntry[]withrequests/tokens remaining + resetsAtprogress bars; previously the fetch 404'd silently
Hermes v0.8.0 (UI changes):
- Logs viewer —
src/screens/logs/logs-screen.tsx(new);src/routes/logs.tsx(new);ConsoleIconnav entry inchat-sidebar.tsx+workspace-shell.tsxtitle; fetches/api/hermes-proxy/api/logs?level=&tail=500; All/Errors tabs, search, color-coded lines, auto-scroll - Delivery failure badge — red
X delivery failurespill on job cards whendelivery_failures > 0;HermesJobtype extended injobs-api.ts - Pre-run script — collapsible textarea in Create Job dialog (
create-job-dialog.tsx);pre_run_scriptwired throughcreateJob()payload
Key design decisions:
- hermes-proxy catch-all (
/api/hermes-proxy/$) forwarded backup and logs calls — no new Studio API routes needed for those - Platform integrations reused the existing
CHAT_PLATFORMS+PlatformsSectionpattern — adding 3 platforms was 30 lines, no new components - SSE breaking change assessment:
use-streaming-message.tsalready correctly handled namedevent:SSE;jobs-screen.tsxEventSource uses a different stream — no fix needed
Design System Consistency (Tasks #DS-1 through #DS-6 + screen migration)
Built a canonical 6-component design system library in src/components/ds/ and migrated all screens to use it.
New components (src/components/ds/):
Card— base surface primitive (3 variants: default, panel, subtle)SettingsRow— label + description + control slot; optional danger borderSectionHeader— section title with optional subtitle, action, and dividerStatusBadge— hugeicon + label in semantic theme color; replaces all emoji status indicatorsListItem— icon + label + description + meta row with consistent hover stateEmptyState— centred icon + title + description + action; used by every empty screen
CSS fixes:
- Added
--theme-hoverto all 9 theme blocks instyles.css - Replaced 300+ broken Tailwind palette classes (
bg-primary-*,border-primary-*,text-black,bg-white) withvar(--theme-*)CSS variables across all screens - Screens migrated: settings/index.tsx, providers-screen.tsx, provider-wizard.tsx, chat-composer.tsx, chat-header.tsx, memory-browser-screen.tsx, knowledge-browser-screen.tsx, profiles-screen.tsx, skills-screen.tsx, workspace-skills-screen.tsx, jobs-screen.tsx, audit-trail-screen.tsx, crews-screen.tsx, files-screen.tsx
Tests: 37 new unit tests for DS components (vitest + @testing-library/react + jsdom); 59 total passing.
The Rule (for future dev): Never use Tailwind color classes on screen-level components. Always use var(--theme-*). See docs/superpowers/specs/2026-04-16-design-system-consistency.md.
Task #19 — Test Suite with visible badges (vitest unit tests + Playwright e2e + CI badges)
Unit tests (vitest):
vitest.config.ts— standalone vitest config (separate fromvite.config.tsto avoid TanStack Start plugin conflicts);nodeenvironment;@alias pointing tosrc/; includessrc/test/**/*.test.tssrc/test/utils.test.ts— 6 tests forcn()(empty args, joins, falsy filtering, Tailwind conflict resolution, clsx object/array syntax)src/test/crew-store.test.ts— 9 tests forcrew-store; usesvi.spyOn(process, 'cwd').mockReturnValue(tmpDir)+vi.resetModules()+ dynamic import pattern to isolate module-levelDATA_DIRper test; covers list, create, update, delete, getCrew, trim, ordering, member assignmentsrc/test/event-store.test.ts— 8 tests forevent-store; same temp-dir isolation pattern; covers appendEvent sequence numbers, getEventsSince, queryAuditEvents (all, by sessionKey, by eventTypes, session list)- All 23 tests pass locally
E2E tests (Playwright):
playwright.config.ts—webServerstartsnode server-entry.json port 3000; reuses existing server outside CI; chromium only; retries on CItests/e2e/smoke.spec.ts— 6 smoke tests: homepage loads,/api/auth-checkreturns JSON,/api/pingresponds (200 or 503 — gateway may be offline in CI), chat/crews/audit pages render without React error boundary
CI (GitHub Actions):
.github/workflows/ci.yml— rewrote from pnpm to npm; two jobs:unit-tests(runsnpm test) ande2e-tests(builds, installs Playwright browsers, runsnpx playwright test); e2e job requires unit-tests to pass first; uploadsplaywright-report/artifact on failurepackage.json— added"test:e2e": "playwright test"script
Badge & version:
- README — added
[](…ci.yml)badge;🧪 Test Suitefeature bullet; version bump in badge - Version: 1.14.0 → 1.15.0
Task #21 — Clone Crew (duplicate an existing crew with one click)
Inspired by xaspx/hermes-control-interface + karmsheel/mission-control-hermes.
Server-side:
src/routes/api/crews/$crewId.clone.ts— newPOST /api/crews/:crewId/clonehandler; reads source crew from store, mints fresh sessions for all members in parallel (same mintSession logic as the create endpoint — supports both enhanced-hermes and portable/local modes), creates a new crew with name"Copy of <original>"and the same goal and member roster; returns 404 if source not found
Client-side:
src/lib/crews-api.ts— addedcloneCrew(crewId)client helper (POST withContent-Type: application/jsonfor CSRF compliance)src/screens/crews/crews-screen.tsx—CrewCardgains aCopy01Iconclone button that appears on hover alongside the existing delete button; both buttons are now in a flex group;cloneMutationadded toCrewsScreen; toast shows"Cloned as 'Copy of …'"on successsrc/screens/crews/crew-detail-screen.tsx— clone button (Copy01Icon) added between Dispatch Task and Delete in the detail header;cloneMutationnavigates to the newly created crew's detail page on successsrc/routeTree.gen.ts— registeredApiCrewsCrewIdCloneRouteas a child ofApiCrewsCrewIdRouteacross all required locations (import, route definition, ApiCrewsCrewIdRouteChildren interface + object, FileRoutesByFullPath/To/ById interfaces, FileRouteTypes unions, FileRoutesByPath declare module)
Version: 1.13.0 → 1.14.0
Task #18 — Audit Trail (timeline of all agent/tool actions)
New cross-session audit timeline accessible at /audit via the sidebar.
Server-side additions:
src/server/event-store.ts— addedqueryAuditEvents(): flexible cross-session query over the SQLite events database; supports filtering by session key, event types, date range, and pagination; also returns the full list of distinct session keys for the session pickersrc/routes/api/audit/index.ts— newGET /api/audit/endpoint; defaults to queryingtool,user_message, andapprovalevents; supportssessionKey,types,since,until,limit,offsetquery params; max 500 results per request
Client-side additions:
src/screens/audit/audit-trail-screen.tsx— full audit trail UI:- Chronological (newest-first) timeline of events, auto-refreshes every 15 seconds
- Session filter dropdown populated from all known sessions
- Event type toggles: Tool Call, User Message, Approval
- Date range presets: Last hour, Last 6h, Last 24h, Last 7d, All time
- Tool event cards show phase badge (start/calling/complete/error) with colour coding; click to expand inline and inspect full args + result (syntax highlighted in monospace, truncated at 2000 chars)
- User message cards show the message text preview
- Approval cards show tool name and status with amber accent
- 50-event pages with Previous/Next pagination and count display
src/routes/audit.tsx— TanStack Start route file for/auditsrc/routeTree.gen.ts— manually registeredAuditRouteandApiAuditIndexRouteacross all required interface locations (FileRoutesByFullPath, FileRoutesByTo, FileRoutesById, FileRouteTypes full/to/id unions, RootRouteChildren, rootRouteChildren object, FileRoutesByPath declare module)src/screens/chat/components/chat-sidebar.tsx— addedTimelineIconimport and Audit Trail nav item after Agentssrc/components/workspace-shell.tsx— added/audit→'Audit Trail'mobile page title
Version: 1.12.0 → 1.13.0
Competitive research + roadmap expansion
Researched all known public Hermes Agent web UI repositories to identify unique selling points and improvement opportunities. Repos reviewed:
- nesquena/hermes-webui — Vanilla JS, no build step, mobile-first, zero framework
- Euraika-Labs/hermesagentwebui — Full CI: lint + unit (vitest) + Playwright e2e, RC quality release, audit trails
- Euraika-Labs/pan-ui — Setup wizard, systemd user service installer, extension system
- joeynyc/hermes-hudui — 13-tab consciousness monitor, WebSocket live updates, command palette, Patterns & Corrections tabs
- xaspx/hermes-control-interface — System metrics (CPU/RAM/disk), agent clone/create, maintenance tools panel, 7-day token chart
- sanchomuzax/hermes-webui — Reads state.db directly for richer analytics; message-level conversation history inspector
- Daniel-Parke/hermes-mission-control — Agent personality file editor (USER.md, MEMORY.md, AGENT.md)
- karmsheel/mission-control-hermes — Hackathon: task tracking, content pipelines, agent clone
Added 11 new planned tasks (#19–#29) to the backlog with attribution notes:
| Task | Attribution |
|---|---|
| #19 Test suite + CI badges | Inspired by Euraika-Labs/hermesagentwebui |
| #20 System health panel | Inspired by xaspx/hermes-control-interface |
| #21 Clone crew | Inspired by xaspx/hermes-control-interface + karmsheel/mission-control-hermes |
| #22 Setup wizard | Inspired by Euraika-Labs/pan-ui |
| #23 Systemd auto-start | Inspired by Euraika-Labs/pan-ui |
| #24 State.db analytics | Inspired by sanchomuzax/hermes-webui |
| #25 Command palette (Ctrl+K) | Inspired by joeynyc/hermes-hudui |
| #26 Identity file editor | Inspired by Daniel-Parke/hermes-mission-control |
| #27 Patterns & corrections viewer | Directly inspired by joeynyc/hermes-hudui |
| #28 Token usage time-series chart | Inspired by xaspx/hermes-control-interface; extended with 7d/30d/90d/1y/custom ranges |
| #29 Session history archive | Inspired by sanchomuzax/hermes-webui |
Updated README roadmap — corrected shipped versions (v1.7.0–v1.12.0) and added all 11 planned items.
Custom Agent Creation & Template Modification
Users can now create their own agents with custom system prompts, identities, and model overrides — and have them automatically appear in the crew builder and template gallery.
New files:
src/types/agent.ts—AgentDefinitioninterface:id,name,emoji,color,roleLabel,systemPrompt,model,tags,isBuiltIn,createdAt,updatedAt; plusCreateAgentInputandUpdateAgentInputtypessrc/server/agent-definitions-store.ts— file-backed CRUD store persisting to.runtime/agent-definitions.json;getBuiltInAgents()derives built-in entries fromAGENT_PERSONASwith pre-written default system prompts;listAgents(),getAgent(),createAgent(),updateAgent(),deleteAgent()src/routes/api/agents/index.ts—GET /api/agents(list),POST /api/agents(create); validates name ≤ 40 chars, color against whitelistsrc/routes/api/agents/$agentId.ts—GET,PATCH,DELETE; returns 403 on attempts to mutate built-in agentssrc/lib/agents-api.ts— client helpers:fetchAgents(),fetchAgent(),createAgent(),updateAgent(),deleteAgent()src/routes/agents.tsx— page route at/agentssrc/screens/agents/agent-library-screen.tsx— Agent Library UI: search, filter (All/Built-in/Custom), agent cards showing emoji, color, role, tags, system prompt preview; Create/Edit/Delete/Duplicate actions; Delete protected for built-ins; stat header (N built-in, N custom)src/screens/agents/agent-editor-dialog.tsx— Create/Edit form: emoji picker grid (24 options), color picker swatch (16 colors), name, role label, system prompt textarea (monospace, resizable), model override, tags (comma-separated); supports both create and edit modes
Modified files:
src/routeTree.gen.ts— registered/agents,/api/agents/,/api/agents/$agentIdroutes in all required TypeScript interfaces and route treesrc/screens/chat/components/chat-sidebar.tsx— addedAiUserIconimport,isAgentsActivestate, "Agents" nav item in the main nav group (below Crews)src/components/workspace-shell.tsx— added/agents→'Agents'mobile page title mappingsrc/screens/crews/components/create-crew-dialog.tsx— fetches full agent list viauseQuery(['agents']); persona picker renders Built-in/Custom<optgroup>sections when custom agents exist;addMember()usesagentOptionsfor next-persona selection; color swatch usesagent.color/agent.emojifrom unified listsrc/screens/crews/components/templates-gallery.tsx— fetches agents viauseQuery(['agents']);TemplateCardreceivesagentsprop; member pills resolve emoji/name from full agent list (custom agents now display correctly)src/routes/api/crews/index.ts— callslistAgents()when minting crew members; resolves emoji, displayName, roleLabel, and model from custom agent if found, falls back to built-in persona; crew member records are now agent-aware
Architecture notes:
- Built-in agents are derived at runtime from
AGENT_PERSONASand never written to disk - Custom agents use UUID IDs; built-in agent IDs follow
builtin-<name>convention GET /api/agentsreturns built-ins first (stable order by persona array), then custom sorted newest-first- The crew creation API is backwards-compatible: a crew member's
personafield remains the lowercase name string; the API layer resolves display metadata from the full agent list at create time
Task #17 — MCP client protocol support (connect to external MCP servers)
The MCP settings screen already existed (752-line UI with add/edit/delete and YAML generation), but it was a draft-only workflow — changes had to be manually copy-pasted into config.yaml. This task completes the integration by wiring the save pathway directly to the config file.
Modified files:
-
src/routes/api/mcp/servers.ts— AddedPUThandler:- Imports
fs,path,os,YAML(same deps ashermes-config.ts) - Added
readConfig(),writeConfig()helpers (local copies of the pattern fromhermes-config.ts) - Added
serversToConfigDict()— inverse of existingreadServers(): convertsMcpServerRecord[]→mcp_serversdict for YAML PUTaccepts{ servers: McpServerRecord[] }, validates each entry, writesconfig.mcp_serversto~/.hermes/config.yaml, returns{ ok, message, servers }
- Imports
-
src/screens/settings/mcp-settings-screen.tsx— UI changes:- Added
savingboolean state - Added
handleSaveToConfig():PUT /api/mcp/servers→ on success:setOriginalServers(servers)+ toast + auto-triggershandleReload()if reload is available - Updated
isDirtybanner: was "copy YAML instruction", now shows a "Save to Config" button (disabled while saving) - Updated header description: removed "until gateway config writes land" placeholder text
- Updated YAML section label to "Manual fallback" with clear context
- Added
Architecture:
- Save writes to local
~/.hermes/config.yaml(same file Hermes reads at startup) - After save, auto-trigger of the existing
/api/mcp/reloadendpoint applies changes live without a full Hermes restart (where supported) - YAML copy-paste fallback retained for environments where Hermes home is on a different machine
- The
isDirtycopy-to-clipboard handler previously also calledsetOriginalServers(servers)— this was removed since the clipboard copy should not mark the local draft as "saved" handleCopySnippetandhandleSaveToConfigboth exist independently; save does NOT copy to clipboard
Task #16 — Cost tracking per crew
Token usage and estimated API cost tracking per crew. A new Usage tab on every crew detail screen shows cumulative input/output tokens per agent and an estimated cost based on a built-in model price table.
New files:
src/types/cost.ts—MemberUsage,CrewUsage,CostStoreDatatypessrc/server/cost-store.ts— file-backed store in.runtime/costs.json; price table for Anthropic/OpenAI/Google models with fuzzy matching;recordMemberUsage()upserts cumulative totals and re-derives crew-level sums;deleteCrewUsage()called on crew deletionsrc/routes/api/crews/$crewId.usage.ts—GET(fetch usage),POST(record member snapshot),DELETE(reset crew usage)src/lib/cost-api.ts— client helpers includingfetchAndRecordUsage()which chains: fetch/api/context-usagefor token data → POST to usage endpoint → invalidate['crew-usage', crewId]querysrc/screens/crews/components/cost-panel.tsx— Usage tab UI: KPI strip (total tokens, input/output split, est. cost), per-agent table with model badges, portable-mode notice, reset button
Modified files:
src/routes/api/context-usage.ts— purely additive: addedinputTokensandoutputTokensto all three return paths; these were already computed (lines 118-119) but not returnedsrc/routes/api/crews/$crewId.ts— addeddeleteCrewUsage(params.crewId)in DELETE handler (alongside existingdeleteWorkflow)src/screens/crews/crew-detail-screen.tsx— added'usage'to tab union type;BarChartIconimport;CostPanelimport;fetchAndRecordUsagecalled inhandleRunEndafter status update; Usage tab in tab bar + body
Architecture decisions:
- Pull-on-done pattern: client queries Hermes session API after each
doneSSE event (no changes tosend-stream.ts) - Token data requires Hermes enhanced mode; portable mode gracefully shows dashes
- Cost store is separate from crew store to avoid bloating
crews.json
Anthropic: claude-opus-4-6/4-5, claude-sonnet-4-6/4-5/4, claude-haiku-4-5/3.5, claude-3-opus OpenAI: gpt-4.1, gpt-4.1-mini, gpt-4o, gpt-4o-mini, gpt-4-turbo, o1, o3-mini Google: gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash
context-usage.tsalready computedinputTokens/outputTokensat lines 118-119 but never returned them — the fix was a two-line addition to the return JSON- Zero-token entries (portable mode) are recorded if context-usage returns any data but filtered out of
fetchAndRecordUsagewith a guard (if (inputTokens === 0 && outputTokens === 0) return)
Task #15 — Agent/crew templates
Pre-built crew configurations that let you jump-start any crew with a known-good composition. A Templates button opens a filterable gallery; selecting a template pre-fills the New Crew dialog and closes the gallery.
New files:
src/types/template.ts—CrewTemplate,CrewTemplateMember,CrewTemplateCategorytypessrc/server/template-store.ts— 7 hardcoded built-in templates + file-backed user templates in.runtime/templates.json; same in-memory/sync-write pattern ascrew-store.tssrc/routes/api/crews/templates/index.ts—GET /api/crews/templates(list),POST /api/crews/templates(create user template) with full validationsrc/routes/api/crews/templates/$id.ts—DELETE /api/crews/templates/:idwith built-in protection (403 on attempts to delete built-ins)src/lib/templates-api.ts—fetchTemplates(),createUserTemplate(),deleteUserTemplate()client helperssrc/screens/crews/components/templates-gallery.tsx— modal gallery with category filter tabs (All / Research / Engineering / Creative / Operations), template cards with persona chip row, "Use Template" and (for user templates) trash-icon delete buttons; TanStack Query['crew-templates']key
Modified files:
src/screens/crews/components/create-crew-dialog.tsx— addedinitialName?,initialGoal?,initialMembers?props;useEffectreset now uses initial values so the dialog reflects the template on opensrc/screens/crews/crews-screen.tsx— addedgalleryOpen,prefilledName/Goal/Membersstate;handleSelectTemplate()chains gallery close → prefill → dialog open; Templates button added to header (usesGridViewIcon);clearPrefill()called on dialog close and after successful create
Built-in templates (7):
| id | name | category | members |
|---|---|---|---|
builtin-research-team |
Research Team | research | Luna (executor), Ada (reviewer), Kai (coordinator) |
builtin-deep-dive |
Deep Dive | research | Luna (executor), Roger (executor), Kai (coordinator) |
builtin-fullstack-squad |
Full-Stack Squad | engineering | Kai (coordinator), Roger, Sally, Max, Ada |
builtin-code-review |
Code Review Crew | engineering | Ada (executor), Luna (reviewer), Nova (specialist) |
builtin-content-studio |
Content Studio | creative | Bill (coordinator), Luna (executor), Roger (reviewer) |
builtin-ops-team |
Ops Team | operations | Max (coordinator), Sally, Kai (executors) |
builtin-sprint-team |
Sprint Team | operations | Kai (coordinator), Roger, Sally (executors), Ada (reviewer) |
GridViewIcon— confirmed to exist in@hugeicons/core-free-iconsCJS bundle before using; many similarly-named icons don't existuseEffectdependency array inCreateCrewDialog—initialMembersadded to deps so resetting works correctly when the same dialog re-opens with a new template; the parent stores prefill in state (not computed inline) to avoid re-render loopsclearPrefill()called in two places —onOpenChange(false)handler andcreateMutation.onSuccess; both paths are needed because users can close the dialog without submitting
Three improvements across two tasks:
Task #12 fix — Knowledge Graph: dialog → split-pane
- The force-directed graph was buried inside a dialog triggered by a small "Graph view" button; most users never found it
- Replaced the dialog with a Pages / Graph toggle in the Knowledge browser header — both views share the left file-tree column; the right pane switches between page content and the graph canvas
- Graph data now fetched eagerly on mount (not lazy); clicking a graph node selects the page and auto-switches back to Pages view
- Dialog imports removed;
GraphCanvasSVG height changed from fixedh-[540px]toh-fullto fill the pane
Task #13 — Crew/agent status dashboard (aggregate metrics)
- Added
StatsStripcomponent at the top of the Crews list screen (above the crew grid) - Six stat chips: Crews, Active (green pulse when >0), Paused, Complete, Agents, Running (green pulse when >0)
RecentActivityFeedbelow: surfaces latestlastActivitysnippets from members across all crews, sorted by recency, max 6 entries- Zero new API calls — all data derived from the
crewsquery already polling every 10 s
Task #14 — Visual Workflow Builder (DAG editor)
Full implementation of a DAG-structured task pipeline editor as a new "Workflow" tab on every crew detail screen.
New files:
src/types/workflow.ts— shared types:WorkflowTask,WorkflowEdge,Workflowsrc/server/workflow-store.ts— file-backed persistence at.runtime/workflows.json, one workflow per crew; same in-memory + deferred disk write pattern as crew-storesrc/routes/api/crews/$crewId.workflow.ts— GET / PUT / DELETE; PUT validates edge references and runs DFS cycle detection (400 on cycle)src/lib/workflow-api.ts— client fetch helperssrc/screens/crews/components/workflow-builder.tsx— the canvas component + runner hook
Modified files:
src/screens/crews/crew-detail-screen.tsx— added "Overview" / "Workflow" tab bar; Workflow tab renders<WorkflowBuilder />src/routes/api/crews/$crewId.ts— crew DELETE now also callsdeleteWorkflow()to keep storage clean
Canvas implementation (pure SVG, no new library deps):
- Nodes:
<rect>(176 × 68 px, r=8) with status tint overlay, task label, assignee text, status badge, input/output port circles - Edges: cubic bezier
<path>with SVG<marker>arrowhead; active edges highlight green; wide invisible hit path for click-to-delete - Pan: pointer capture on SVG background drag
- Zoom: non-passive wheel listener (0.2×–4×) + toolbar +/−/⊙ buttons
- Node drag:
data-tidattribute hit-test + pointer capture, delta converted via viewBox ratio
Interactions:
- Add Task — toolbar button opens modal: label, prompt (textarea), assignee (crew member select)
- Connect mode — toolbar toggle; click source node (highlights), click target node → creates directed edge; cycle check runs before adding; Esc cancels
- Auto Layout — Kahn's BFS topological layers; nodes spread left-to-right in columns, vertically centred per layer
- Edit Task — double-click node or "Edit Task" button in side panel; edit label/prompt/assignee
- Delete Task — removes node and all its connected edges
- Delete Edge — click edge (wide transparent hit area) or × button in side panel dependency list
- Save — explicit "Save" button; only enabled when dirty; persists to
.runtime/workflows.jsonvia PUT - Clear — delete entire workflow with confirm()
Execution engine (client-side, no new server state):
topoLayers()— Kahn's BFS producingstring[][]where each inner array is a parallel execution layer- "Run Workflow" dispatches layer 0 in parallel via existing
dispatchTask()API; tracks sessionKey→taskId in apendingRefMap - Separate
EventSource('/api/chat-events')opened while running;run_end/doneevents matched by sessionKey; on task completion the layer completion check fires - When all tasks in a layer complete, the next layer dispatches automatically
- If any task errors: execution halts, error shown in toolbar, remaining layers skipped
- Per-node visual status updates in real time: idle → running (green border) → done (indigo) / error (red)
TypeScript: zero new errors (build passes clean; 4 pre-existing errors in unrelated files unchanged)
- Branch:
main - Version: 1.8.0
- Task #15: Agent/crew templates — pre-built configurations (nice-to-have)
- Task #16: Cost tracking per crew (nice-to-have)
- Task #17: MCP client protocol support — connect to external MCP servers (critical)
- Task #18: Audit trail — timeline of all agent/crew actions (critical)
- Hermes gateway updated (399 new commits, v0.8.0 → current; 78 new bundled skills)
- Compatibility audit against the update — identified 4 gaps
- Closed all 4 gaps
Gap 1 — Config migration (v13 → v16)
- Ran
hermes config migrateto apply 3 version bumps:- v13→14: migrated legacy flat
stt.modelto provider-specific section - v14→15: added
display.interim_assistant_messages: true - v15→16: renamed
display.tool_progress_overrides→display.platforms
- v13→14: migrated legacy flat
- The
--quietflag doesn't exist; migration was invoked via Python directly to work around a skill-config probe crash that exited before the version bump
Gap 2 — Status messages toggle
- Added "Status messages"
Switchrow to Display settings - Reads/writes
display.interim_assistant_messages— controls whether the gateway shows natural mid-turn assistant status messages - Slotted between Streaming and Show reasoning rows in
src/routes/settings/index.tsx
Gap 3 — Live run streaming in Jobs UI
POST /api/jobs/{job_id}/runhas norun_idreturn value;/v1/runsis a separate parallel runner- Used
/v1/runsas the "Run now" execution path for live feedback; scheduled cron runs still go through job system - New Studio server routes:
src/routes/api/hermes-runs.ts— POST proxy to/v1/runssrc/routes/api/hermes-runs.$runId.events.ts— SSE passthrough proxy to/v1/runs/{runId}/events
src/lib/jobs-api.ts: addedstartRun(prompt)→ run_id,RunEventtypesrc/screens/jobs/jobs-screen.tsx:formatRunEventLabel()maps backend event names to human labelsJobCardgainsactiveRunIdstate,useEffectsubscribing toEventSource, live log + response text accumulator, auto-expand on trigger- "Run now" button calls
startRun(), falls back to fire-and-forget on failure - Expanded panel switches between "Live run" (pulsing indicator + event log) and "Run history"
- Both routes registered in
src/routeTree.gen.ts(7 locations: imports, constants, 3 interfaces, RoutesById, rootRouteChildren)
Gap 4 — Session reset + per-platform display overrides in Settings
AddPlatformOverridecomponent added: dropdown of 13 known platforms, add/remove overrides- Agent Behavior section: session reset mode selector (
none/daily/idle/both) + conditional "Reset hour" and "Idle timeout" inputs - Display section: per-platform
tool_progressoverrides editor (all/new only/verbose/off per platform)
TypeScript: zero new errors (npx tsc --noEmit — only 5 pre-existing errors in unrelated files)
- Branch:
dev - Version: 1.7.0
- Task 6 (Feature 1 — Approvals UI): remaining items — "Approve for Session" scope button, resolved-approval receipt in message timeline, global approval badge in sidebar
- Task 7 (Feature 4 — Permissions & Config UI):
command_allowlisteditor, website blocklist domain editor,quick_commandseditor, chat platform tokens in Integrations
- Completed Task 8: Session Persistence via Redis
Research findings:
local-session-store.tsalready existed with correct logic but was dead code — never imported by any API route- All session/history routes returned empty data in portable mode (gateway unavailable)
send-stream.tsstreamed messages but never saved them anywhere in portable modeioredisnot previously installed; file-based.runtime/local-sessions.jsonapproach was designed but inactive
What was implemented:
local-session-store.tsextended with optional Redis backend:tryInitRedis()— non-blocking async init; pings Redis and merges Redis data into in-memory storeloadFromRedis()/saveSessionToRedis()/appendMessageToRedis()/deleteSessionFromRedis()helpers- Redis key schema:
hermes:studio:sessions(hash),hermes:studio:messages:{id}(list), 30-day TTL - Graceful fallback: if
REDIS_URLunset or Redis unreachable, file store used transparently
/api/sessions— all 4 verbs wired to local store when gateway unavailable:- GET: returns
listLocalSessions()with session metadata - POST: calls
ensureLocalSession(friendlyId, model)— persisted immediately - PATCH: calls
updateLocalSessionTitle(sessionKey, label)— persistent rename - DELETE: calls
deleteLocalSession(sessionKey)— removes from file + Redis
- GET: returns
/api/history— when gateway unavailable: resolves session key (explicit → latest → 'new'), returnsgetLocalMessages().map(toLocalChatMessage)send-stream.ts— portable mode now saves messages:- Before stream:
ensureLocalSession(key)+appendLocalMessage({ role: 'user', ... }) - After stream:
appendLocalMessage({ role: 'assistant', content: accumulated })
- Before stream:
ioredisadded as runtime dependency viapnpm add ioredis.env.exampleupdated withREDIS_URL=redis://localhost:6379comment block
Tests passed (standalone Node.js test script):
- Session create + file written ✅
- Reload from disk after memory clear (server restart simulation) ✅
- Messages preserved across reload ✅
- Delete session ✅
- 500-message cap enforcement ✅
- TypeScript: zero errors ✅
- Build: clean ✅
- Branch:
dev→ merged tomain - Version: 1.5.0
- Task 9: Multi-Agent Orchestration Dashboard
- Completed Task 7: Permissions & Toolsets Settings UI
Research findings:
~/.hermes/config.yamlhas rich permissions/sandbox fields:approvals,security,toolsets,code_execution,agent.reasoning_effort,agent.verbose- Existing settings page already has Agent Behavior (max_turns, gateway_timeout, tool_use_enforcement) but not these
- Existing
PATCH /api/hermes-configdeep-merges config changes — no new backend needed HermesConfigSectioncomponent already handles multi-view dispatch viasectionContentmap
What was implemented:
'permissions'added toSettingsSectionIdtype union{ id: 'permissions', label: 'Permissions & Toolsets' }added toSETTINGS_NAV_ITEMS(appears after "Agent Behavior")HermesConfigSection activeView="permissions"wired into content arearenderPermissions()function with 4 sub-sections:- Approvals: mode (manual/auto/off) + timeout slider
- Toolsets: list of active toolsets as removable tags + add-custom input
- Security: redact_secrets, tirith_enabled, website_blocklist toggles
- Code Execution: timeout + max_tool_calls number inputs
- Agent Reasoning: reasoning_effort (low/medium/high) + verbose toggle
- State for
newToolsetinput hoisted toHermesConfigSectioncomponent level (hooks rule compliance) LockIconimported from@hugeicons/core-free-icons
Tests passed:
- TypeScript: zero errors ✅
- Build: clean (3.76s) ✅
- Branch:
dev - Version: 1.4.0
- Task 8: Session Persistence via Redis
- Research what session state is currently stored and where
- Design Redis adapter for session/history persistence
- Implement Redis connection + session store
- Completed Task 6: Cron Job Manager UI (confirmed already shipped in codebase)
- Updated README.md: all "Hermes Workspace" → "Hermes Studio", clone URLs, Docker commands, roadmap, features section, star history chart, version badge 1.0.0 → 1.3.0
- Bumped package.json version: 1.0.0 → 1.3.0
- Updated CHANGELOG.md with v1.3.0 entry (Task 6)
- Committed and pushed to GitHub
Task 6 research findings:
- Jobs UI was already fully implemented in hermes-workspace and carried over cleanly
GET/POST /api/hermes-jobsandGET/POST/PATCH/DELETE /api/hermes-jobs/$jobIdproxy routes completejobs-api.tscovers: fetchJobs, createJob, updateJob, deleteJob, pauseJob, resumeJob, triggerJob, fetchJobOutputJobsScreenwired into workspace-shell.tsx nav and mobile-tab-bar.tsx- Feature gate: shows BackendUnavailableState if gateway lacks
/api/jobs - Auto-refresh every 30s via React Query; run history expandable per card
- Branch:
dev - Version: 1.3.0
- Task 7: Permissions & Sandbox Config UI ✅ (see Session 5)
- Completed Task 5: Skill Installation from web UI
Research findings:
POST /api/skills/installandPOST /api/skills/uninstallalready existed and workedPOST /api/skills(toggle) was a 501 Not Implemented stubclawhubCLI is NOT installed on this machineGET /api/skillscorrectly returns skill lists from gateway- Full install/uninstall/toggle UI was already in
skills-screen.tsx— wired to the endpoints
What was implemented:
POST /api/skillstoggle action: reads/writes~/.hermes/skills/.studio-prefs.jsonto track disabled skill IDs; does not require gatewayGET /api/skillsmerges local prefs to report accurateenabledstatePOST /api/skills/install: now tries Hermes gateway native endpoint first, then clawhub CLI, then returnsinstallClawhub: 'pip install skillhub'if clawhub is missingPOST /api/skills/uninstall: added path traversal security guard- UI: loading spinners (⏳) on action buttons while in progress
- UI: "Installing... may take up to 2 minutes" progress hint
- UI: clawhub-missing inline banner with
pip install skillhubinstructions + dismiss - UI: success toasts on install/uninstall completion
- Branding: "Hermes Workspace" → "Hermes Studio" in header and security badge
Tests passed:
- Toggle disable/enable prefs file round-trip: ✅
- Install with missing clawhub → returns hint: ✅
- Uninstall path traversal attack blocked: ✅
- TypeScript: zero errors ✅
- Build: clean ✅
- Live API tests via pnpm dev: all 5 scenarios ✅
- Branch:
dev - Version: 1.2.0
- Task 6: Cron Job Manager UI ✅ (confirmed already shipped — see Session 4)
- Task 7: Permissions & Sandbox Config UI
- Completed Task 4: Execution Approvals UI (full end-to-end implementation)
- Deep-dived hermes gateway approval mechanism: agent blocks via threading.Event in tools/approval.py; resolved via
/approveor/denychat commands; gateway has no native HTTP approval endpoints (sessions capability is false) - Rewrote
src/lib/approvals-store.tsfrom no-op stub to real in-memory Map with sessionStorage persistence - Updated
src/routes/api/send-stream.tsto translateapproval.required/tool.approval/exec.approvalgateway SSE events → clientapprovalevent - Created
src/routes/api/approvals.$approvalId.approve.ts— dual strategy: native gateway endpoint first, then chat command/approve [scope]fallback - Created
src/routes/api/approvals.$approvalId.deny.ts— same pattern, sends/deny - Updated
src/screens/chat/hooks/use-streaming-message.ts— addedonApprovalRequestoption andcase 'approval'SSE handler - Updated
src/screens/chat/chat-screen.tsx— extractedhandleApprovalRequestshared callback, wired into bothuseRealtimeChatHistoryanduseStreamingMessage, added "Always Allow" button to approval banner UI, updatedresolvePendingApprovalto passscopebody param - Updated
src/routeTree.gen.ts— manually registered both new approval routes (TanStack Router codegen not running) - TypeScript: zero errors (
npx tsc --noEmitclean) - Build: clean (
pnpm build✓ in 3.51s)
- Branch:
dev - Version: 1.1.0
- Package manager: pnpm
- Task 5: Skill installation from web UI
- Add "Install" button to skills explorer that calls POST /api/skills/install
- Show installation state (pending / installed / error) per skill
- Possibly browse and install from hermes hub registry
- Forked hermes-workspace v1.0.0 (MIT) as Hermes Studio
- Stripped upstream git history, started clean
mainbranch - Removed internal planning docs (workspace-final-markdown-review.md, FUTURE-FEATURES.md)
- Updated package.json: name, description, author, homepage, repository
- Updated LICENSE: dual attribution JPeetz + outsourc-e
- Updated README: rebranded, added "What's different" section, acknowledgments
- Created
devbranch for active development - Pushed to https://github.com/JPeetz/Hermes-Studio
- Set 14 GitHub topics for discoverability
- Installed 59 custom openfang skills into ~/.hermes/skills/openfang/ (8 sub-categories)
- Fixed 4 skills missing SKILL.md (api-design, code-review-guide, moltspeak, writing-style)
- Installed superpowers plugin v5.0.7 for Claude Code
- Branch:
dev(active development) - Last commit:
f1b7ce2feat: initial release of Hermes Studio v1.0.0 - Node: 24 local / 22 CI
- Package manager: pnpm
- Task 4: Execution Approvals UI
- Research hermes approval events in gateway API
- Check src/lib/approvals-store.ts and src/routes/api/send.ts (501 stub)
- Design modal: command shown → Allow once / Always allow / Deny