hermes-war-room-ui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHermes War Room UI
Hermes War Room UI
Skill by ara.so — Hermes Skills collection.
Hermes War Room is a browser-based visual dashboard for managing Hermes Agent's multi-profile orchestration and kanban delegation system. It provides real-time monitoring of agent operatives, task boards, and mission coordination—transforming command-line agent orchestration into a visual "operations floor."
由ara.so开发的Skill——Hermes Skills集合。
Hermes War Room是一个基于浏览器的可视化仪表板,用于管理Hermes Agent的多配置文件编排和看板委派系统。它提供对Agent执行体、任务看板和任务协作的实时监控,将命令行式的Agent编排转化为可视化的“操作指挥中心”。
What It Does
功能介绍
The War Room wraps the Hermes CLI with a Vue/Nuxt interface that:
- Visualizes active agents as operatives on a floor with live status
- Displays kanban tasks in a 4-column board (Todo/Ready/Running/Blocked)
- Manages missions via chat with an orchestrator agent
- Auto-nudges the orchestrator when delegated tasks complete
- Edits profiles (SOUL.md, skills, tools) directly from the UI
- Tracks delegation chains showing parent/child task relationships
It reads from and , shells out to the CLI, and persists UI state in its own SQLite database.
~/.hermes/kanban.db~/.hermes/profiles/hermes该指挥中心基于Vue/Nuxt界面封装Hermes CLI,具备以下功能:
- 可视化活跃Agent:将Agent展示为指挥中心内的执行体,显示实时状态
- 看板任务展示:以四列看板(待办/就绪/运行中/阻塞)展示任务
- 任务协作管理:通过与编排Agent对话管理任务
- 自动提醒编排Agent:当委派任务完成时自动通知编排Agent
- UI直接编辑配置文件:可在界面中直接编辑SOUL.md、skills和tools
- 跟踪委派链:展示父/子任务的关联关系
它读取和目录,调用 CLI,并将UI状态存储在自身的SQLite数据库中。
~/.hermes/kanban.db~/.hermes/profiles/hermesInstallation
安装步骤
Quick Start (Production Binary)
快速启动(生产二进制包)
bash
undefinedbash
undefinedDownload and extract the latest release
下载并解压最新版本
mkdir -p ~/hermes-war-room && cd ~/hermes-war-room
curl -L -o hermes-war-room.tar.gz
https://github.com/Naroh091/hermes-war-room/releases/latest/download/hermes-war-room.tar.gz tar xzf hermes-war-room.tar.gz
https://github.com/Naroh091/hermes-war-room/releases/latest/download/hermes-war-room.tar.gz tar xzf hermes-war-room.tar.gz
mkdir -p ~/hermes-war-room && cd ~/hermes-war-room
curl -L -o hermes-war-room.tar.gz
https://github.com/Naroh091/hermes-war-room/releases/latest/download/hermes-war-room.tar.gz tar xzf hermes-war-room.tar.gz
https://github.com/Naroh091/hermes-war-room/releases/latest/download/hermes-war-room.tar.gz tar xzf hermes-war-room.tar.gz
Run (requires Hermes CLI installed and Node 22+)
运行(需已安装Hermes CLI和Node 22+)
HERMES_HOME=$HOME/.hermes NITRO_HOST=127.0.0.1 NITRO_PORT=3000
node .output/server/index.mjs
node .output/server/index.mjs
Open `http://localhost:3000` in your browser.HERMES_HOME=$HOME/.hermes NITRO_HOST=127.0.0.1 NITRO_PORT=3000
node .output/server/index.mjs
node .output/server/index.mjs
在浏览器中打开`http://localhost:3000`。Development Setup
开发环境搭建
bash
git clone https://github.com/Naroh091/hermes-war-room.git
cd hermes-war-room
pnpm install
pnpm devThe dev server runs on .
http://localhost:3000bash
git clone https://github.com/Naroh091/hermes-war-room.git
cd hermes-war-room
pnpm install
pnpm dev开发服务器运行在。
http://localhost:3000Environment Variables
环境变量
bash
undefinedbash
undefinedRequired
必填项
HERMES_HOME=$HOME/.hermes # Path to Hermes profiles and kanban DB
NITRO_HOST=127.0.0.1 # Bind address
NITRO_PORT=3000 # Port
HERMES_HOME=$HOME/.hermes # Hermes配置文件和看板数据库路径
NITRO_HOST=127.0.0.1 # 绑定地址
NITRO_PORT=3000 # 端口
Optional
可选项
WAR_ROOM_DB=./data/war-room.db # War Room's own SQLite database
NODE_ENV=production # Production/development mode
undefinedWAR_ROOM_DB=./data/war-room.db # War Room自身的SQLite数据库路径
NODE_ENV=production # 生产/开发模式
undefinedKey Concepts
核心概念
Operatives (Profiles)
执行体(配置文件)
Each Hermes profile becomes an "operative" in the War Room:
vue
<!-- Components displaying operatives -->
<OperativeWorkstation
:operative="operative"
:current-task="currentTask"
@click="openDossier"
/>Operatives have:
- Callsign: Display name (e.g., "LIDER", "INVESTIGADOR")
- Avatar: Notionist character image
- Color: UI accent color
- Status: Standing by / Working / Blocked
- Profile slug: Maps to
~/.hermes/profiles/<slug>/
每个Hermes配置文件在指挥中心中对应一个“执行体”:
vue
<!-- 展示执行体的组件 -->
<OperativeWorkstation
:operative="operative"
:current-task="currentTask"
@click="openDossier"
/>执行体包含以下属性:
- 呼号:显示名称(例如:"LIDER", "INVESTIGADOR")
- 头像:Notionist风格的角色图片
- 颜色:UI强调色
- 状态:待命/工作中/阻塞
- 配置文件别名:对应路径
~/.hermes/profiles/<slug>/
Orchestrator Pattern
编排模式
Convention is to have one "leader" profile that routes but never executes:
markdown
<!-- profiles/lider/SOUL.md -->
You are the mission orchestrator. Your job is to:
1. Decompose user goals into discrete tasks
2. Delegate each task via kanban to the right specialist
3. Never do the work yourself
4. Summarize results when workers complete tasks
Always use the terminal tool to create kanban tasks:
hermes kanban create --assignee <worker> --title "..." --body "..." --jsonWorkers have full tool access and execute:
markdown
<!-- profiles/investigador/SOUL.md -->
You are a research specialist. When assigned a kanban task:
1. Execute the research using available tools
2. Complete the task with: hermes kanban complete <id> --summary "..."常规配置是设置一个“领导者”配置文件,它仅负责路由任务,从不执行任务:
markdown
<!-- profiles/lider/SOUL.md -->
你是任务编排者。你的职责是:
1. 将用户目标分解为独立任务
2. 通过看板将每个任务委派给合适的专家
3. 绝不亲自执行任务
4. 当执行者完成任务后汇总结果
必须使用终端工具创建看板任务:
hermes kanban create --assignee <worker> --title "..." --body "..." --json执行者拥有完整的工具权限并执行任务:
markdown
<!-- profiles/investigador/SOUL.md -->
你是研究专家。当收到看板任务时:
1. 使用可用工具执行研究
2. 通过以下命令完成任务:hermes kanban complete <id> --summary "..."Missions
任务协作
A mission is a conversation thread with an orchestrator:
typescript
// server/api/missions/create.post.ts
export default defineEventHandler(async (event) => {
const { orchestratorId, title } = await readBody(event)
const mission = await db.insert(missions).values({
orchestrator_id: orchestratorId,
title: title,
status: 'open',
created_at: new Date()
}).returning()
return mission[0]
})Messages are sent via ACP sessions that persist across turns.
任务协作是与编排Agent的对话线程:
typescript
// server/api/missions/create.post.ts
export default defineEventHandler(async (event) => {
const { orchestratorId, title } = await readBody(event)
const mission = await db.insert(missions).values({
orchestrator_id: orchestratorId,
title: title,
status: 'open',
created_at: new Date()
}).returning()
return mission[0]
})消息通过ACP会话发送,会话会在多轮对话中保持持久化。
Server API Endpoints
服务器API端点
Missions
任务协作
typescript
// Create a new mission
POST /api/missions/create
{
"orchestratorId": 1,
"title": "Research competitor pricing"
}
// Send a message (returns SSE stream)
POST /api/missions/:id/message
{
"content": "Find pricing for Acme Corp products"
}
// Get mission history
GET /api/missions/:idtypescript
// 创建新任务协作
POST /api/missions/create
{
"orchestratorId": 1,
"title": "研究竞争对手定价"
}
// 发送消息(返回SSE流)
POST /api/missions/:id/message
{
"content": "查找Acme Corp产品的定价"
}
// 获取任务协作历史
GET /api/missions/:idOperatives
执行体
typescript
// List all operatives
GET /api/operatives
// Get operative details with current task
GET /api/operatives/:id
// Update operative (callsign, avatar, color, active)
PATCH /api/operatives/:id
{
"callsign": "RESEARCHER",
"color": "#3b82f6",
"active": true
}
// Retrain (update SOUL.md, skills, tools)
POST /api/operatives/:id/retrain
{
"soul": "You are a specialized legal analyst...",
"skills": ["research", "analysis"],
"mcpServers": ["filesystem", "brave-search"]
}typescript
// 列出所有执行体
GET /api/operatives
// 获取执行体详情及当前任务
GET /api/operatives/:id
// 更新执行体(呼号、头像、颜色、激活状态)
PATCH /api/operatives/:id
{
"callsign": "RESEARCHER",
"color": "#3b82f6",
"active": true
}
// 重新训练(更新SOUL.md、skills、tools)
POST /api/operatives/:id/retrain
{
"soul": "你是专业的法律分析师...",
"skills": ["research", "analysis"],
"mcpServers": ["filesystem", "brave-search"]
}Kanban
看板
typescript
// Get all tasks for the board view
GET /api/kanban/tasks
// Get tasks for a specific operative
GET /api/kanban/tasks?assignee=investigador
// Watch a task (auto-nudge when complete)
POST /api/kanban/watch
{
"taskId": "task_abc123",
"missionId": 5
}typescript
// 获取看板视图的所有任务
GET /api/kanban/tasks
// 获取指定执行体的任务
GET /api/kanban/tasks?assignee=investigador
// 监控任务(完成时自动提醒)
POST /api/kanban/watch
{
"taskId": "task_abc123",
"missionId": 5
}Creating an Orchestration Team
创建编排团队
1. Set Up Profiles
1. 设置配置文件
Create an orchestrator and workers via Hermes CLI:
bash
undefined通过Hermes CLI创建编排者和执行者:
bash
undefinedCreate orchestrator
创建编排者
hermes profile create lider --model claude-3-5-sonnet-20241022
hermes profile create lider --model claude-3-5-sonnet-20241022
Create specialist workers
创建专业执行者
hermes profile create investigador --model claude-3-5-sonnet-20241022
hermes profile create legal --model claude-3-5-sonnet-20241022
hermes profile create writer --model gpt-4o
undefinedhermes profile create investigador --model claude-3-5-sonnet-20241022
hermes profile create legal --model claude-3-5-sonnet-20241022
hermes profile create writer --model gpt-4o
undefined2. Configure SOULs
2. 配置SOUL文件
Edit :
~/.hermes/profiles/lider/SOUL.mdmarkdown
undefined编辑:
~/.hermes/profiles/lider/SOUL.mdmarkdown
undefinedMission Orchestrator
任务编排者
You coordinate a team of specialists. When the user gives you a goal:
- Break it into discrete, parallelizable tasks
- Assign each task to the right specialist via kanban
- Never do the work yourself
- Wait for task completion notifications
- Synthesize results into a final answer
你负责协调一组专家。当用户给出目标时:
- 将目标分解为可并行处理的独立任务
- 通过看板将每个任务分配给合适的专家
- 绝不亲自执行任务
- 等待任务完成通知
- 将结果整合为最终答案
Available Team
可用团队成员
- investigador: Research, data gathering, fact-checking
- legal: Legal analysis, compliance review
- writer: Content creation, documentation
- investigador:研究、数据收集、事实核查
- legal:法律分析、合规审查
- writer:内容创作、文档编写
Task Creation
任务创建格式
Always use this exact format:
bash
hermes kanban create \
--assignee <specialist> \
--title "<concise title>" \
--body "<detailed instructions>" \
--json
Edit `~/.hermes/profiles/investigador/SOUL.md`:
```markdown必须使用以下精确格式:
bash
hermes kanban create \
--assignee <specialist> \
--title "<简洁标题>" \
--body "<详细说明>" \
--json
编辑`~/.hermes/profiles/investigador/SOUL.md`:
```markdownResearch Specialist
研究专家
You execute research tasks assigned via kanban.
When you receive a task:
- Read the task body for full context
- Use available tools (search, filesystem) to gather information
- Complete with a detailed summary:
bash
hermes kanban complete <task_id> --summary "<findings>"
undefined你负责执行通过看板委派的研究任务。
收到任务时:
- 阅读任务主体获取完整上下文
- 使用可用工具(搜索、文件系统)收集信息
- 通过以下命令完成任务并提交详细摘要:
bash
hermes kanban complete <task_id> --summary "<研究结果>"
undefined3. Configure Skills in War Room UI
3. 在War Room UI中配置Skills
- Navigate to
/team - Click the operative's badge
- Click "Retrain"
- Enable skills:
- Orchestrator: (for kanban),
terminalreasoning - Workers: ,
terminal,filesystem,search, etc.reasoning
- Orchestrator:
- Add MCP servers as needed
- Save
- 导航到页面
/team - 点击执行体的徽章
- 点击“重新训练”
- 启用对应Skills:
- 编排者:(用于看板)、
terminalreasoning - 执行者:、
terminal、filesystem、search等reasoning
- 编排者:
- 根据需要添加MCP服务器
- 保存配置
4. Start the Dispatcher
4. 启动调度器
The dispatcher claims tasks and hands them to workers:
readybash
undefined调度器会认领“就绪”状态的任务并分配给执行者:
bash
undefinedIn a tmux/screen session
在tmux/screen会话中运行
hermes gateway
Without the dispatcher, tasks will stay in "ready" state.hermes gateway
如果没有调度器,任务将一直处于“就绪”状态。Real Usage Example
实际使用示例
Complete Flow: Research Mission
完整流程:研究任务协作
typescript
// 1. Frontend: Create mission
const mission = await $fetch('/api/missions/create', {
method: 'POST',
body: {
orchestratorId: 1, // lider
title: 'Market research for Product X'
}
})
// 2. Frontend: Send initial brief
const eventSource = new EventSource(
`/api/missions/${mission.id}/message`,
{
method: 'POST',
body: JSON.stringify({
content: 'Research our top 3 competitors: pricing, features, and market position'
})
}
)
eventSource.addEventListener('tool_call', (event) => {
const data = JSON.parse(event.data)
if (data.name === 'terminal' && data.input.includes('kanban create')) {
console.log('Orchestrator delegating task...')
}
})
// 3. Backend: Orchestrator creates tasks
// (Automatically intercepted by War Room)
// hermes kanban create --assignee investigador --title "Research Competitor A" --json
// hermes kanban create --assignee investigador --title "Research Competitor B" --json
// 4. Backend: Workers process tasks
// investigador picks up tasks via dispatcher, completes them
// 5. Backend: Auto-nudge when tasks complete
// War Room injects system message:
// "Tasks task_abc123, task_def456 completed. Summarize for the user."
// 6. Frontend: Orchestrator synthesizes response
eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data)
console.log('Orchestrator:', data.content)
// "Based on the research, here's what we found about your competitors..."
})typescript
// 1. 前端:创建任务协作
const mission = await $fetch('/api/missions/create', {
method: 'POST',
body: {
orchestratorId: 1, // lider
title: '产品X的市场调研'
}
})
// 2. 前端:发送初始简报
const eventSource = new EventSource(
`/api/missions/${mission.id}/message`,
{
method: 'POST',
body: JSON.stringify({
content: '研究我们的三大竞争对手:定价、功能和市场地位'
})
}
)
eventSource.addEventListener('tool_call', (event) => {
const data = JSON.parse(event.data)
if (data.name === 'terminal' && data.input.includes('kanban create')) {
console.log('编排者正在委派任务...')
}
})
// 3. 后端:编排者创建任务
// (由War Room自动拦截)
// hermes kanban create --assignee investigador --title "研究竞争对手A" --json
// hermes kanban create --assignee investigador --title "研究竞争对手B" --json
// 4. 后端:执行者处理任务
// investigador通过调度器领取任务并完成
// 5. 后端:任务完成时自动提醒
// War Room注入系统消息:
// "任务task_abc123、task_def456已完成。请为用户汇总结果。"
// 6. 前端:编排者整合响应
eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data)
console.log('编排者回复:', data.content)
// "根据调研结果,以下是我们对竞争对手的分析..."
})Server-Side Task Watcher
服务器端任务监控器
typescript
// server/utils/taskWatcher.ts
import { db } from './db'
import { watchedTasks, missions } from './schema'
export async function pollWatchedTasks() {
const watched = await db.select().from(watchedTasks).where(
eq(watchedTasks.notified, false)
)
for (const watch of watched) {
const task = await getKanbanTask(watch.task_id)
if (task.status === 'done' || task.status === 'blocked') {
// Inject system message into orchestrator session
const mission = await db.select().from(missions).where(
eq(missions.id, watch.mission_id)
).get()
await sendSystemMessage(mission.acp_session_id, {
role: 'system',
content: `Task ${watch.task_id} is now ${task.status}. Summary: ${task.summary}. Please integrate this into your response to the user.`
})
// Mark as notified
await db.update(watchedTasks).set({ notified: true }).where(
eq(watchedTasks.id, watch.id)
)
}
}
}
// Run every 5 seconds
setInterval(pollWatchedTasks, 5000)typescript
// server/utils/taskWatcher.ts
import { db } from './db'
import { watchedTasks, missions } from './schema'
export async function pollWatchedTasks() {
const watched = await db.select().from(watchedTasks).where(
eq(watchedTasks.notified, false)
)
for (const watch of watched) {
const task = await getKanbanTask(watch.task_id)
if (task.status === 'done' || task.status === 'blocked') {
// 向编排者会话注入系统消息
const mission = await db.select().from(missions).where(
eq(missions.id, watch.mission_id)
).get()
await sendSystemMessage(mission.acp_session_id, {
role: 'system',
content: `任务${watch.task_id}当前状态为${task.status}。摘要:${task.summary}。请将此内容整合到给用户的回复中。`
})
// 标记为已通知
await db.update(watchedTasks).set({ notified: true }).where(
eq(watchedTasks.id, watch.id)
)
}
}
}
// 每5秒运行一次
setInterval(pollWatchedTasks, 5000)Composables (Frontend)
组合式函数(前端)
useMission
useMission
vue
<script setup>
const { mission, messages, sendMessage, loading } = useMission(missionId)
async function briefOrchestrator() {
await sendMessage('Analyze legal compliance for EU markets')
}
</script>
<template>
<div>
<ChatMessage
v-for="msg in messages"
:key="msg.id"
:message="msg"
/>
<ChatInput @send="sendMessage" :disabled="loading" />
</div>
</template>vue
<script setup>
const { mission, messages, sendMessage, loading } = useMission(missionId)
async function briefOrchestrator() {
await sendMessage('分析欧盟市场的合规性')
}
</script>
<template>
<div>
<ChatMessage
v-for="msg in messages"
:key="msg.id"
:message="msg"
/>
<ChatInput @send="sendMessage" :disabled="loading" />
</div>
</template>useOperatives
useOperatives
vue
<script setup>
const { operatives, activeOperatives, refresh } = useOperatives()
const workers = computed(() =>
activeOperatives.value.filter(op => op.profile_slug !== 'lider')
)
</script>
<template>
<OperativeWorkstation
v-for="op in workers"
:key="op.id"
:operative="op"
/>
</template>vue
<script setup>
const { operatives, activeOperatives, refresh } = useOperatives()
const workers = computed(() =>
activeOperatives.value.filter(op => op.profile_slug !== 'lider')
)
</script>
<template>
<OperativeWorkstation
v-for="op in workers"
:key="op.id"
:operative="op"
/>
</template>useKanban
useKanban
vue
<script setup>
const { tasks, tasksByStatus, refresh } = useKanban()
const columns = {
todo: computed(() => tasksByStatus.value.todo),
ready: computed(() => tasksByStatus.value.ready),
running: computed(() => tasksByStatus.value.running),
blocked: computed(() => tasksByStatus.value.blocked)
}
</script>
<template>
<div class="kanban-board">
<KanbanColumn
v-for="(tasks, status) in columns"
:key="status"
:title="status"
:tasks="tasks"
/>
</div>
</template>vue
<script setup>
const { tasks, tasksByStatus, refresh } = useKanban()
const columns = {
todo: computed(() => tasksByStatus.value.todo),
ready: computed(() => tasksByStatus.value.ready),
running: computed(() => tasksByStatus.value.running),
blocked: computed(() => tasksByStatus.value.blocked)
}
</script>
<template>
<div class="kanban-board">
<KanbanColumn
v-for="(tasks, status) in columns"
:key="status"
:title="status"
:tasks="tasks"
/>
</div>
</template>Common Patterns
常见模式
Hiring a New Operative
新增执行体
typescript
// POST /api/operatives/create
export default defineEventHandler(async (event) => {
const { slug, callsign, cloneFrom } = await readBody(event)
// Shell out to Hermes CLI
const { stdout } = await execAsync(
`hermes profile create ${slug} ${cloneFrom ? `--clone ${cloneFrom}` : ''}`
)
// Register in War Room DB
const operative = await db.insert(operatives).values({
profile_slug: slug,
callsign: callsign || slug.toUpperCase(),
avatar: randomAvatar(),
color: randomColor(),
active: true
}).returning()
return operative[0]
})typescript
// POST /api/operatives/create
export default defineEventHandler(async (event) => {
const { slug, callsign, cloneFrom } = await readBody(event)
// 调用Hermes CLI
const { stdout } = await execAsync(
`hermes profile create ${slug} ${cloneFrom ? `--clone ${cloneFrom}` : ''}`
)
// 在War Room数据库中注册
const operative = await db.insert(operatives).values({
profile_slug: slug,
callsign: callsign || slug.toUpperCase(),
avatar: randomAvatar(),
color: randomColor(),
active: true
}).returning()
return operative[0]
})Live Status Updates
实时状态更新
vue
<!-- components/OperativeWorkstation.vue -->
<script setup>
const props = defineProps(['operative'])
const { data: currentTask } = useFetch(`/api/operatives/${props.operative.id}/current-task`, {
watch: true,
refreshInterval: 2000 // Poll every 2s
})
const status = computed(() => {
if (!currentTask.value) return 'Standing by'
if (currentTask.value.status === 'running') return `Working on: ${currentTask.value.title}`
if (currentTask.value.status === 'blocked') return 'Blocked'
return 'Standing by'
})
</script>
<template>
<div class="workstation" :style="{ borderColor: operative.color }">
<img :src="operative.avatar" />
<div class="status-pill">{{ status }}</div>
</div>
</template>vue
<!-- components/OperativeWorkstation.vue -->
<script setup>
const props = defineProps(['operative'])
const { data: currentTask } = useFetch(`/api/operatives/${props.operative.id}/current-task`, {
watch: true,
refreshInterval: 2000 // 每2秒轮询一次
})
const status = computed(() => {
if (!currentTask.value) return '待命'
if (currentTask.value.status === 'running') return `正在处理:${currentTask.value.title}`
if (currentTask.value.status === 'blocked') return '阻塞'
return '待命'
})
</script>
<template>
<div class="workstation" :style="{ borderColor: operative.color }">
<img :src="operative.avatar" />
<div class="status-pill">{{ status }}</div>
</div>
</template>Delegation Chain Visualization
委派链可视化
vue
<script setup>
const props = defineProps(['taskId'])
const { data: chain } = useFetch(`/api/kanban/tasks/${props.taskId}/chain`)
</script>
<template>
<div class="delegation-chain">
<div v-for="task in chain" :key="task.id" class="chain-node">
<OperativeBadge :operative="task.assignee" mini />
<div class="task-info">
<strong>{{ task.title }}</strong>
<span class="status">{{ task.status }}</span>
</div>
<div v-if="task.children?.length" class="children">
<DelegationChain
v-for="child in task.children"
:key="child.id"
:task-id="child.id"
/>
</div>
</div>
</div>
</template>vue
<script setup>
const props = defineProps(['taskId'])
const { data: chain } = useFetch(`/api/kanban/tasks/${props.taskId}/chain`)
</script>
<template>
<div class="delegation-chain">
<div v-for="task in chain" :key="task.id" class="chain-node">
<OperativeBadge :operative="task.assignee" mini />
<div class="task-info">
<strong>{{ task.title }}</strong>
<span class="status">{{ task.status }}</span>
</div>
<div v-if="task.children?.length" class="children">
<DelegationChain
v-for="child in task.children"
:key="child.id"
:task-id="child.id"
/>
</div>
</div>
</div>
</template>Troubleshooting
故障排除
"No dispatcher detected" Warning
提示“未检测到调度器”
Problem: Tasks stuck in "ready" state, workers never pick them up.
Solution: Start the Hermes gateway:
bash
hermes gatewayRun in a persistent session (tmux/screen) or as a systemd service.
问题:任务一直处于“就绪”状态,执行者从未领取任务。
解决方案:启动Hermes网关:
bash
hermes gateway在持久会话(tmux/screen)中运行,或配置为systemd服务。
Orchestrator Does Work Instead of Delegating
编排者亲自执行任务而非委派
Problem: Orchestrator answers directly instead of creating kanban tasks.
Solution: Update the orchestrator's SOUL.md to be more explicit:
markdown
CRITICAL RULES:
1. You MUST delegate via kanban. NEVER answer directly.
2. Every user request becomes one or more kanban tasks.
3. Use the terminal tool: hermes kanban create --assignee <worker> --title "..." --body "..." --json
4. After delegating, say "Tasks assigned, waiting for results."Also ensure the skill is enabled and the orchestrator's chat includes the delegation preamble (injected automatically by War Room).
terminal问题:编排者直接回复用户,而非创建看板任务。
解决方案:更新编排者的SOUL.md,明确规则:
markdown
核心规则:
1. 必须通过看板委派任务,绝不直接回复。
2. 用户的每个请求都要转化为一个或多个看板任务。
3. 使用终端工具:hermes kanban create --assignee <worker> --title "..." --body "..." --json
4. 委派完成后,回复“任务已分配,等待结果。”同时确保 Skill已启用,且编排者的对话包含委派前置说明(由War Room自动注入)。
terminalTasks Not Auto-Completing
任务完成后未自动通知
Problem: Worker completes task but orchestrator never sees it.
Solution: Verify the task watcher is running (built into War Room server). Check logs for:
[taskWatcher] Detected task task_abc123 completed, nudging orchestratorIf missing, restart the War Room server.
问题:执行者完成任务,但编排者未收到通知。
解决方案:确认任务监控器正在运行(内置在War Room服务器中)。检查日志是否包含:
[taskWatcher] 检测到任务task_abc123已完成,正在提醒编排者如果没有,重启War Room服务器。
Profile Changes Not Reflected
配置文件更改未生效
Problem: Updated SOUL.md or skills in UI but agent still behaves the same.
Solution: The ACP session caches profile config. Either:
- Wait for the current task to complete (new session picks up changes)
- Restart the dispatcher:
pkill -f "hermes gateway" && hermes gateway
问题:在UI中更新了SOUL.md或Skills,但Agent行为未改变。
解决方案:ACP会话会缓存配置文件信息。可选择:
- 等待当前任务完成(新会话会加载更改后的配置)
- 重启调度器:
pkill -f "hermes gateway" && hermes gateway
Database Lock Errors
数据库锁定错误
Problem: when reading kanban.db.
database is lockedSolution: Hermes uses SQLite in WAL mode, but heavy concurrent access can still cause locks. Ensure:
- War Room and Hermes share the same
HERMES_HOME - Only one dispatcher is running
- No manual sessions are open on kanban.db
sqlite3
问题:读取kanban.db时出现错误。
database is locked解决方案:Hermes使用WAL模式的SQLite,但高并发访问仍可能导致锁定。确保:
- War Room和Hermes使用相同的路径
HERMES_HOME - 仅运行一个调度器
- 没有手动打开的会话连接到kanban.db
sqlite3
Production Deployment
生产部署
Systemd Service
Systemd服务
ini
undefinedini
undefined/etc/systemd/system/hermes-war-room.service
/etc/systemd/system/hermes-war-room.service
[Unit]
Description=Hermes War Room UI
After=network.target
[Service]
Type=simple
User=hermes
WorkingDirectory=/opt/hermes-war-room
Environment="HERMES_HOME=/home/hermes/.hermes"
Environment="NITRO_HOST=0.0.0.0"
Environment="NITRO_PORT=3000"
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node .output/server/index.mjs
Restart=on-failure
[Install]
WantedBy=multi-user.target
```bash
sudo systemctl enable hermes-war-room
sudo systemctl start hermes-war-room[Unit]
Description=Hermes War Room UI
After=network.target
[Service]
Type=simple
User=hermes
WorkingDirectory=/opt/hermes-war-room
Environment="HERMES_HOME=/home/hermes/.hermes"
Environment="NITRO_HOST=0.0.0.0"
Environment="NITRO_PORT=3000"
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node .output/server/index.mjs
Restart=on-failure
[Install]
WantedBy=multi-user.target
```bash
sudo systemctl enable hermes-war-room
sudo systemctl start hermes-war-roomReverse Proxy (nginx)
反向代理(nginx)
nginx
server {
listen 80;
server_name war-room.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# SSE requires no buffering
proxy_buffering off;
proxy_cache off;
}
}nginx
server {
listen 80;
server_name war-room.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# SSE需要禁用缓冲
proxy_buffering off;
proxy_cache off;
}
}Environment-Specific Configs
环境特定配置
bash
undefinedbash
undefined.env.production
.env.production
HERMES_HOME=/var/lib/hermes
NITRO_HOST=127.0.0.1
NITRO_PORT=3000
WAR_ROOM_DB=/var/lib/hermes-war-room/war-room.db
NODE_ENV=production
undefinedHERMES_HOME=/var/lib/hermes
NITRO_HOST=127.0.0.1
NITRO_PORT=3000
WAR_ROOM_DB=/var/lib/hermes-war-room/war-room.db
NODE_ENV=production
undefinedKey Files
核心文件结构
hermes-war-room/
├── server/
│ ├── api/
│ │ ├── missions/ # Mission CRUD + messaging
│ │ ├── operatives/ # Operative management + retrain
│ │ └── kanban/ # Kanban task queries + watch
│ ├── utils/
│ │ ├── db.ts # War Room SQLite connection
│ │ ├── hermes.ts # Hermes CLI wrappers
│ │ └── taskWatcher.ts # Auto-nudge poller
│ └── middleware/
├── components/
│ ├── OperativeWorkstation.vue
│ ├── KanbanBoard.vue
│ ├── MissionChat.vue
│ └── OperativeDossier.vue
├── composables/
│ ├── useMission.ts
│ ├── useOperatives.ts
│ └── useKanban.ts
├── pages/
│ ├── index.vue # War Room floor
│ ├── team.vue # Roster/badges
│ └── missions.vue # Archive
└── data/
└── war-room.db # War Room state (not Hermes kanban.db)The War Room reads and directly but never writes to them—writes go through the CLI.
~/.hermes/kanban.db~/.hermes/profiles/hermeshermes-war-room/
├── server/
│ ├── api/
│ │ ├── missions/ # 任务协作的增删改查及消息功能
│ │ ├── operatives/ # 执行体管理及重新训练
│ │ └── kanban/ # 看板任务查询及监控
│ ├── utils/
│ │ ├── db.ts # War Room SQLite连接
│ │ ├── hermes.ts # Hermes CLI封装
│ │ └── taskWatcher.ts # 自动提醒轮询器
│ └── middleware/
├── components/
│ ├── OperativeWorkstation.vue
│ ├── KanbanBoard.vue
│ ├── MissionChat.vue
│ └── OperativeDossier.vue
├── composables/
│ ├── useMission.ts
│ ├── useOperatives.ts
│ └── useKanban.ts
├── pages/
│ ├── index.vue # 指挥中心主界面
│ ├── team.vue # 执行体列表
│ └── missions.vue # 任务协作归档
└── data/
└── war-room.db # War Room状态数据库(非Hermes的kanban.db)War Room直接读取和目录,但从不直接写入这些路径——所有写入操作都通过 CLI完成。
~/.hermes/kanban.db~/.hermes/profiles/hermes