auto-save 2026-05-11 18:34 (~5)

This commit is contained in:
2026-05-11 18:34:56 +08:00
parent 111dbfe6a5
commit 405746dd72
5 changed files with 232 additions and 83 deletions

View File

@@ -1,12 +1,5 @@
{ {
"entries": [ "entries": [
{
"files_changed": 1,
"hash": "a94dc90",
"message": "auto-save 2026-05-10 09:37 (~1)",
"ts": "2026-05-10T09:37:59+08:00",
"type": "commit"
},
{ {
"files_changed": 1, "files_changed": 1,
"message": "Codex 会话活跃 · 最近命令codex · 分支 master · 1 项未提交变更 · 最近提交auto-save 2026-05-10 09:37 (~1)", "message": "Codex 会话活跃 · 最近命令codex · 分支 master · 1 项未提交变更 · 最近提交auto-save 2026-05-10 09:37 (~1)",
@@ -3268,6 +3261,13 @@
"type": "session-heartbeat", "type": "session-heartbeat",
"message": "Codex 会话活跃 · 最近命令codex · 分支 master · 1 项未提交变更 · 最近提交auto-save 2026-05-11 18:23 (~1)", "message": "Codex 会话活跃 · 最近命令codex · 分支 master · 1 项未提交变更 · 最近提交auto-save 2026-05-11 18:23 (~1)",
"files_changed": 1 "files_changed": 1
},
{
"ts": "2026-05-11T18:29:22+08:00",
"type": "commit",
"message": "auto-save 2026-05-11 18:29 (~2)",
"hash": "111dbfe",
"files_changed": 2
} }
] ]
} }

View File

@@ -14,13 +14,14 @@
- 飞书事件回调(`cli_a97764e101b95be9`https://hermes.kang-kang.com/feishu/events/cli_a97764e101b95be9 - 飞书事件回调(`cli_a97764e101b95be9`https://hermes.kang-kang.com/feishu/events/cli_a97764e101b95be9
- 飞书主动通知https://hermes.kang-kang.com/feishu/notify - 飞书主动通知https://hermes.kang-kang.com/feishu/notify
- 飞书机器人列表https://hermes.kang-kang.com/feishu/apps展示 App ID / 回调地址,不含 Secret / Token - 飞书机器人列表https://hermes.kang-kang.com/feishu/apps展示 App ID / 回调地址,不含 Secret / Token
- 爱马仕前端「集成 → 飞书集成」可自助添加 / 更新 / 删除飞书机器人Secret / Token 只写入服务器 `/etc/hermes-feishu-bridge.env` - 爱马仕前端「网关 → 飞书集成」可自助添加 / 更新 / 删除飞书机器人Secret / Token 只写入服务器 `/etc/hermes-feishu-bridge.env`
- 飞书事件消息回复默认走 Feishu `im/v1/messages/{message_id}/reply`;主动通知仍走 `/feishu/notify` - 飞书事件消息回复默认走 Feishu `im/v1/messages/{message_id}/reply`;主动通知仍走 `/feishu/notify`
- 爱马仕前端「仪表盘」同步了上游 Hermes 的快捷入口板块个人版展示主站、API、飞书机器人列表、文档/解析入口 - 爱马仕前端「工作区」同步了上游 Hermes 的快捷入口板块个人版展示主站、API、飞书机器人列表、文档/解析入口
- 爱马仕前端「仪表盘」活动热力图已重做为带摘要、月份标尺、紧凑格子和细分色阶的活动卡片 - 爱马仕前端「工作区」活动热力图已重做为带摘要、月份标尺、紧凑格子和细分色阶的活动卡片
- 爱马仕前端「设置 → 连接」可自助维护 API 地址 / API Key 并测试连接;「对话 → 存周报」和「设置 → 周报记录」会在本地保存任务描述、上下文片段和最终周报 - 爱马仕前端「设置 → 连接」可自助维护 API 地址 / API Key 并测试连接;「对话 → 存周报」和「设置 → 周报记录」会在本地保存任务描述、上下文片段和最终周报
- 爱马仕前端「模型」可维护 AI 模型 Profiles、Provider、Base URL 和 LXC 内 `/opt/hermes-agent/config.yaml``model` 块,保存后重启 Docker `hermes-agent` - 爱马仕前端「模型」可维护 AI 模型 Profiles,用于给不同 Agent 绑定不同模型、Provider、Base URL 和服务器端 Key 引用
- 爱马仕前端「工具集 → MCP 工具接入」可维护 LXC 内 `/opt/hermes-agent/config.yaml``mcp_servers` 块,保存后重启 Docker `hermes-agent` - 爱马仕前端「提供商」可维护 LXC 内 `/opt/hermes-agent/config.yaml``model` 块,保存后重启 Docker `hermes-agent`
- 爱马仕前端「工具 → MCP 工具接入」可维护 LXC 内 `/opt/hermes-agent/config.yaml``mcp_servers` 块,保存后重启 Docker `hermes-agent`
- 飞书、模型、MCP、共享 Agent/Profile 等 `/feishu/*` 管理接口复用网页登录 cookie并在前端 401 时走 `/_auth/verify` 静默续期 - 飞书、模型、MCP、共享 Agent/Profile 等 `/feishu/*` 管理接口复用网页登录 cookie并在前端 401 时走 `/_auth/verify` 静默续期
- 当前前端不再启用 Service Worker 静态壳缓存;`sw.js` 仅用于清理旧 `hermes-ui-*` 缓存并注销旧注册 - 当前前端不再启用 Service Worker 静态壳缓存;`sw.js` 仅用于清理旧 `hermes-ui-*` 缓存并注销旧注册
- 文档 / 解析https://styles.kang-kang.com - 文档 / 解析https://styles.kang-kang.com

View File

@@ -110,7 +110,7 @@ document.addEventListener("DOMContentLoaded", () => {
safeBoot("绑定导航", bindTabs); safeBoot("绑定导航", bindTabs);
safeBoot("绑定对话", bindChat); safeBoot("绑定对话", bindChat);
safeBoot("绑定搜索", bindSearch); safeBoot("绑定搜索", bindSearch);
safeBoot("绑定 Skill Studio", bindStudio); safeBoot("绑定技能库", bindStudio);
safeBoot("渲染侧栏", renderSidebar); safeBoot("渲染侧栏", renderSidebar);
safeBoot("渲染对话", renderChat); safeBoot("渲染对话", renderChat);
safeBoot("渲染智能体", renderAgents); safeBoot("渲染智能体", renderAgents);
@@ -223,7 +223,7 @@ function updateModelDisplay(modelValue, providerValue = "") {
const statSub = document.getElementById("statModelSub"); const statSub = document.getElementById("statModelSub");
const aboutModel = document.getElementById("aboutModelValue"); const aboutModel = document.getElementById("aboutModelValue");
if (stat) stat.textContent = label; if (stat) stat.textContent = label;
if (statSub) statSub.textContent = provider ? "Provider: " + provider : "Provider 以设置为准"; if (statSub) statSub.textContent = provider ? "Provider: " + provider : "Provider 以提供商页为准";
if (aboutModel) aboutModel.textContent = provider ? model + " · " + provider : model; if (aboutModel) aboutModel.textContent = provider ? model + " · " + provider : model;
} }
@@ -626,9 +626,19 @@ function bindTabs() {
function restoreActiveTab() { function restoreActiveTab() {
const saved = localStorage.getItem(LS_TAB); const saved = localStorage.getItem(LS_TAB);
if (!saved) return; if (!saved) return;
if (!document.querySelector(`.side-item[data-tab="${CSS.escape(saved)}"]`)) return; const aliases = {
if (!document.getElementById("tab-" + saved)) return; research: "sessions",
switchTab(saved, { persist: false }); agent: "agents",
dashboard: "office",
studio: "skills",
cron: "schedules",
integrations: "gateway",
};
let next = aliases[saved] || saved;
if (!document.querySelector(`.side-item[data-tab="${CSS.escape(next)}"]`) || !document.getElementById("tab-" + next)) {
next = "chat";
}
switchTab(next, { persist: false });
} }
let _dashboardDirty = true; let _dashboardDirty = true;
function markDashboardDirty() { _dashboardDirty = true; } function markDashboardDirty() { _dashboardDirty = true; }
@@ -638,29 +648,35 @@ function switchTab(name, options = {}) {
document.querySelectorAll(".side-item").forEach(t => t.classList.toggle("active", t.dataset.tab === name)); document.querySelectorAll(".side-item").forEach(t => t.classList.toggle("active", t.dataset.tab === name));
document.querySelectorAll(".tab-panel").forEach(p => p.classList.toggle("active", p.id === "tab-" + name)); document.querySelectorAll(".tab-panel").forEach(p => p.classList.toggle("active", p.id === "tab-" + name));
if (name === "chat") setTimeout(() => document.getElementById("chatInput")?.focus(), 50); if (name === "chat") setTimeout(() => document.getElementById("chatInput")?.focus(), 50);
if (name === "studio") { if (name === "sessions") {
renderSessionsPanel();
refreshMemory();
}
if (name === "skills") {
renderStudioLibrary(); renderStudioLibrary();
renderStudioCanvas(); renderStudioCanvas();
} }
if (name === "cron") refreshCron(); if (name === "schedules") refreshCron();
if (name === "memory") refreshMemory(); if (name === "soul") refreshMemory();
if (name === "models") { if (name === "models") {
renderModelProfiles(); renderModelProfiles();
refreshUiConfig().catch((error) => { refreshUiConfig().catch((error) => {
setSharedConfigStatus("共享配置读取失败: " + (error.message || error), true); setSharedConfigStatus("共享配置读取失败: " + (error.message || error), true);
}); });
}
if (name === "providers") {
refreshHermesConfig(); refreshHermesConfig();
} }
if (name === "tools") { if (name === "tools") {
refreshTools(); refreshTools();
refreshHermesConfig(); refreshHermesConfig();
} }
if (name === "integrations") refreshFeishuApps(); if (name === "gateway") refreshFeishuApps();
if (name === "settings") { if (name === "settings") {
renderWeeklyReports(); renderWeeklyReports();
} }
if (name === "runs") setTimeout(() => document.getElementById("runPrompt")?.focus(), 50); if (name === "runs") setTimeout(() => document.getElementById("runPrompt")?.focus(), 50);
if (name === "dashboard" && _dashboardDirty) { if (name === "office" && _dashboardDirty) {
// 推迟到下一帧,避免阻塞切换动画 // 推迟到下一帧,避免阻塞切换动画
requestAnimationFrame(() => { requestAnimationFrame(() => {
refreshDashboard(); refreshDashboard();
@@ -2103,7 +2119,7 @@ function renderDashStats() {
if (avatarEl) avatarEl.textContent = a.emoji || "🤖"; if (avatarEl) avatarEl.textContent = a.emoji || "🤖";
} else { } else {
setText("stTopAgent", "还未使用智能体"); setText("stTopAgent", "还未使用智能体");
setText("stTopAgentSub", "去 Agent 面板创建一个开始对话"); setText("stTopAgentSub", "去档案面板创建一个开始对话");
if (avatarEl) avatarEl.textContent = "—"; if (avatarEl) avatarEl.textContent = "—";
} }
} }
@@ -2204,7 +2220,7 @@ function renderHeatmap() {
// 窗口尺寸变化时重新渲染热力图 // 窗口尺寸变化时重新渲染热力图
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
if (document.getElementById("tab-dashboard")?.classList.contains("active")) { if (document.getElementById("tab-office")?.classList.contains("active")) {
renderHeatmap(); renderHeatmap();
} }
}); });
@@ -4227,22 +4243,62 @@ function handleRunEvent(evt) {
addRunEvent(shortType, body, cls); addRunEvent(shortType, body, cls);
} }
// ========== Memory (真实 Hermes SOUL.md + sessions 快照) ========== // ========== Sessions / Soul / Memory ==========
function renderSessionsPanel() {
const list = document.getElementById("localSessionsList");
if (!list) return;
const ids = sortedConvoIds();
list.innerHTML = "";
if (!ids.length) {
list.innerHTML = '<div class="history-empty">还没有本地会话</div>';
return;
}
for (const id of ids) {
const c = state.conversations[id];
if (!c) continue;
const row = document.createElement("div");
row.className = "session-row clickable";
row.title = "点击打开这个本地会话";
const time = new Date(c.updatedAt || c.createdAt || Date.now()).toLocaleString("zh-CN", {
month: "numeric",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
});
row.innerHTML = `
<div class="session-row-title"></div>
<div class="session-row-meta">
<span></span>
<span></span>
<span></span>
<span class="session-row-action">→ 打开</span>
</div>
`;
row.querySelector(".session-row-title").textContent = c.title || "未命名会话";
const metas = row.querySelectorAll(".session-row-meta span");
metas[0].textContent = time;
metas[1].textContent = (c.messages?.length || 0) + " 条消息";
metas[2].textContent = c.agentId && state.agents[c.agentId] ? state.agents[c.agentId].name : "默认档案";
row.onclick = () => switchConvo(id);
list.appendChild(row);
}
}
async function refreshMemory() { async function refreshMemory() {
const soulEl = document.getElementById("memorySoul"); const soulEl = document.getElementById("memorySoul");
const sessEl = document.getElementById("memorySessions"); const sessEl = document.getElementById("memorySessions");
const cntEl = document.getElementById("memorySessCount"); const cntEl = document.getElementById("memorySessCount");
if (!soulEl || !sessEl) return; if (!soulEl && !sessEl) return;
try { try {
const [soulRes, sessRes] = await Promise.all([ const [soulRes, sessRes] = await Promise.all([
fetch("/memory/SOUL.md", { cache: "no-store" }), soulEl ? fetch("/memory/SOUL.md", { cache: "no-store" }) : Promise.resolve(null),
fetch("/memory/sessions.json", { cache: "no-store" }), sessEl ? fetch("/memory/sessions.json", { cache: "no-store" }) : Promise.resolve(null),
]); ]);
soulEl.textContent = soulRes.ok ? await soulRes.text() : "(无 SOUL.md)"; if (soulEl) soulEl.textContent = soulRes?.ok ? await soulRes.text() : "(无 SOUL.md)";
if (sessRes.ok) { if (sessEl && sessRes?.ok) {
const data = await sessRes.json(); const data = await sessRes.json();
const list = data.sessions || []; const list = data.sessions || [];
cntEl.textContent = list.length; if (cntEl) cntEl.textContent = list.length;
sessEl.innerHTML = ""; sessEl.innerHTML = "";
if (!list.length) { if (!list.length) {
sessEl.innerHTML = '<div class="history-empty">还没有会话</div>'; sessEl.innerHTML = '<div class="history-empty">还没有会话</div>';
@@ -4272,7 +4328,8 @@ async function refreshMemory() {
} }
} }
} catch (e) { } catch (e) {
soulEl.textContent = "加载失败: " + (e.message || e); if (soulEl) soulEl.textContent = "加载失败: " + (e.message || e);
if (sessEl) sessEl.innerHTML = '<div class="history-empty">加载失败: ' + escapeHTML(e.message || e) + '</div>';
} }
} }

View File

@@ -11,7 +11,7 @@
<link rel="icon" type="image/svg+xml" href="./icon.svg"> <link rel="icon" type="image/svg+xml" href="./icon.svg">
<link rel="apple-touch-icon" href="./icon.svg"> <link rel="apple-touch-icon" href="./icon.svg">
<title>爱马仕 · AI</title> <title>爱马仕 · AI</title>
<link rel="stylesheet" href="./styles.css?v=20260511-settings-ia-v35"> <link rel="stylesheet" href="./styles.css?v=20260511-modules-ia-v36">
</head> </head>
<body> <body>
@@ -617,13 +617,13 @@
</div> </div>
</div> </div>
<!-- TAB: 记忆 (真实 Hermes SOUL.md + sessions 快照) --> <!-- TAB: 人格 (真实 Hermes SOUL.md) -->
<section class="tab-panel" id="tab-memory"> <section class="tab-panel" id="tab-soul">
<div class="panel-head"> <div class="panel-head">
<div class="panel-head-row"> <div class="panel-head-row">
<div> <div>
<h2>记忆</h2> <h2>人格</h2>
<p>Hermes 后端 <code>SOUL.md</code> + 会话历史快照 · 每分钟自动同步</p> <p>通过 <code>SOUL.md</code> 定义 Agent 的人格、语气和长期指令。</p>
</div> </div>
<div class="panel-head-actions"> <div class="panel-head-actions">
<button class="glass-btn-sm" onclick="refreshMemory()"> <button class="glass-btn-sm" onclick="refreshMemory()">
@@ -639,8 +639,102 @@
<pre class="memory-body" id="memorySoul">加载中…</pre> <pre class="memory-body" id="memorySoul">加载中…</pre>
</div> </div>
<div class="memory-pane"> <div class="memory-pane">
<div class="memory-pane-head">会话历史 · <span id="memorySessCount">0</span></div> <div class="memory-pane-head">说明</div>
<div class="memory-sessions" id="memorySessions">加载中…</div> <div class="memory-sessions">
<div class="settings-help">每次对话会加载这份长期指令。编辑能力暂未开放在前端,当前页面用于读取线上 Hermes 同步快照。</div>
<button class="glass-btn-sm" onclick="switchTab('chat');fillPrompt('请总结当前 SOUL.md 的人格设定和约束')">让 Agent 解读人格</button>
</div>
</div>
</div>
</section>
<!-- TAB: 记忆 -->
<section class="tab-panel" id="tab-memory">
<div class="panel-head">
<div class="panel-head-row">
<div>
<h2>记忆</h2>
<p>Hermes 在不同会话之间沉淀的偏好、事实和可检索上下文。</p>
</div>
</div>
</div>
<div class="memory-grid">
<div class="memory-pane">
<div class="memory-pane-head">代理记忆</div>
<div class="memory-sessions">
<div class="settings-help">当前个人版已同步 SOUL.md 和会话快照;更细的外部记忆 Provider 仍由 Hermes 后端配置。</div>
<button class="glass-btn-sm" onclick="switchTab('soul')">查看人格</button>
</div>
</div>
<div class="memory-pane">
<div class="memory-pane-head">相关入口</div>
<div class="memory-sessions">
<button class="glass-btn-sm" onclick="switchTab('sessions')">查看会话</button>
<button class="glass-btn-sm" onclick="switchTab('tools');fillPrompt('列出当前可用的记忆相关工具')">查看记忆工具</button>
</div>
</div>
</div>
</section>
<!-- TAB: 提供商 -->
<section class="tab-panel" id="tab-providers">
<div class="panel-head">
<div class="panel-head-row">
<div>
<h2>提供商</h2>
<p>配置 LLM Provider、运行模型、Base URL 和服务器端 API Key 引用。</p>
</div>
<div class="panel-head-actions">
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15.5-6.3L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 21v-5h5"/></svg>
读取配置
</button>
</div>
</div>
</div>
<div class="models-scroll settings-scroll">
<div class="settings-group wide">
<div class="settings-group-head">
<div class="settings-group-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 7h.01"/><path d="M7.5 7h3.75a3.75 3.75 0 0 1 0 7.5H9L6 18v-3.5H5A3.5 3.5 0 0 1 5 7h2.5z"/><path d="M15 14.5h1a3.5 3.5 0 0 0 0-7h-1"/></svg>
</div>
<div>
<div class="settings-group-title">运行 Provider</div>
<div class="settings-group-desc">线上 Hermes agent 当前默认调用的模型和 Provider 配置</div>
</div>
</div>
<div class="settings-group-body">
<div class="settings-grid-3">
<div class="settings-field">
<label for="hermesModelDefault">默认模型 ID</label>
<input type="text" id="hermesModelDefault" placeholder="google/gemini-3.1-pro-preview" autocomplete="off">
</div>
<div class="settings-field">
<label for="hermesModelProvider">Provider</label>
<input type="text" id="hermesModelProvider" placeholder="openrouter" autocomplete="off">
</div>
<div class="settings-field">
<label for="hermesModelBaseUrl">Base URL</label>
<input type="text" id="hermesModelBaseUrl" placeholder="https://openrouter.ai/api/v1" autocomplete="off">
</div>
</div>
<div class="settings-grid-3">
<div class="settings-field">
<label for="hermesModelApiKeyRef">AI API Key</label>
<input type="password" id="hermesModelApiKeyRef" placeholder="服务器环境变量,不在浏览器显示" disabled>
<div class="settings-help">真实 Key 仍只放在服务器环境变量;这里不保存、不回显密钥。多 Agent 的专属 Key 引用在「模型」Profile 里维护。</div>
</div>
</div>
<div class="settings-actions">
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">读取 Provider 配置</button>
<button class="glass-btn-sm primary" id="hermesModelSaveBtn" onclick="saveModelConfig()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></svg>
保存 Provider 并重启
</button>
<div class="settings-help" id="hermesModelStatus">打开提供商页后自动读取。</div>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@@ -856,14 +950,18 @@
<thead><tr><th>Tab</th><th>调用</th><th>数据源</th></tr></thead> <thead><tr><th>Tab</th><th>调用</th><th>数据源</th></tr></thead>
<tbody> <tbody>
<tr><td>对话</td><td><code>POST /v1/chat/completions</code> (SSE 流式)</td><td>实时 · 前端 localStorage 存会话</td></tr> <tr><td>对话</td><td><code>POST /v1/chat/completions</code> (SSE 流式)</td><td>实时 · 前端 localStorage 存会话</td></tr>
<tr><td>研究</td><td>跳回对话 + 预填 prompt</td><td>纯前端</td></tr> <tr><td>会话</td><td>本地对话列表 + <code>/memory/sessions.json</code> + <code>/memory/sessions/{id}.json</code></td><td>localStorage + systemd timer sync</td></tr>
<tr><td>Agent</td><td>前端 CRUD + system prompt 组装</td><td>localStorage (hermes-ui-agents-v1)</td></tr> <tr><td>档案</td><td>前端 CRUD + system prompt 组装</td><td>localStorage (hermes-ui-agents-v1)</td></tr>
<tr><td>Skill Studio</td><td><code>GET /hermes-skills/</code> autoindex JSON</td><td>docker cp 快照 (78 个真实 SKILL.md)</td></tr> <tr><td>工作区</td><td><code>GET /v1/models</code> + localStorage 聚合</td><td>实时 + 本地</td></tr>
<tr><td>定时任务</td><td><code>GET/POST/PATCH/DELETE /api/jobs</code></td><td>Hermes 后端实时</td></tr> <tr><td>模型</td><td><code>GET/PUT /feishu/shared-config</code></td><td>服务器共享模型 Profiles</td></tr>
<tr><td>记忆</td><td><code>GET /memory/SOUL.md</code> + <code>/memory/sessions.json</code> + <code>/memory/sessions/{id}.json</code></td><td>systemd timer 每 1 分钟 sync</td></tr> <tr><td>提供商</td><td><code>GET/PUT /feishu/hermes-config</code> <code>model</code></td><td>Hermes 运行模型配置</td></tr>
<tr><td>工具</td><td><code>GET /memory/tools.txt</code></td><td><code>hermes tools list</code> 输出每分钟刷新</td></tr> <tr><td>技能</td><td><code>GET /hermes-skills/</code> autoindex JSON</td><td>docker cp 快照 (78 个真实 SKILL.md)</td></tr>
<tr><td>人格</td><td><code>GET /memory/SOUL.md</code></td><td>systemd timer sync</td></tr>
<tr><td>记忆</td><td><code>GET /memory/sessions.json</code> 入口聚合</td><td>systemd timer sync</td></tr>
<tr><td>工具</td><td><code>GET /memory/tools.txt</code> + <code>GET/PUT /feishu/hermes-config</code><code>mcp_servers</code></td><td><code>hermes tools list</code> + MCP 配置</td></tr>
<tr><td>计划任务</td><td><code>GET/POST/PATCH/DELETE /api/jobs</code></td><td>Hermes 后端实时</td></tr>
<tr><td>网关</td><td><code>GET/POST/DELETE /feishu/apps</code></td><td>外部消息入口</td></tr>
<tr><td>异步任务</td><td><code>POST /v1/runs</code> + EventSource <code>/v1/runs/{id}/events</code></td><td>实时 SSE</td></tr> <tr><td>异步任务</td><td><code>POST /v1/runs</code> + EventSource <code>/v1/runs/{id}/events</code></td><td>实时 SSE</td></tr>
<tr><td>仪表盘</td><td><code>GET /v1/models</code> + localStorage 聚合</td><td>实时 + 本地</td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -879,7 +977,7 @@
</div> </div>
<div class="help-card"> <div class="help-card">
<h3>🧠 Skill Studio 编排原理</h3> <h3>🧠 技能编排原理</h3>
<p>不是真的在后端运行编排,而是前端<b>组装复合 system prompt</b>:</p> <p>不是真的在后端运行编排,而是前端<b>组装复合 system prompt</b>:</p>
<pre class="md-pre"><code>Agent.stages.pre → 阶段一 · 前置(理解目标) <pre class="md-pre"><code>Agent.stages.pre → 阶段一 · 前置(理解目标)
Agent.stages.exec → 阶段二 · 执行(工具调用 / 生成) Agent.stages.exec → 阶段二 · 执行(工具调用 / 生成)
@@ -928,47 +1026,39 @@ git push # Gitea kangwan/hermes-glass-ui-personal
</div> </div>
</section> </section>
<!-- TAB: 研究 --> <!-- TAB: 会话 -->
<section class="tab-panel" id="tab-research"> <section class="tab-panel" id="tab-sessions">
<div class="panel-head"> <div class="panel-head">
<h2>研究</h2> <div class="panel-head-row">
<p>爱马仕能帮你深度调研、解读文档、调用工具。</p> <div>
<h2>会话</h2>
<p>查看本地对话和 Hermes 后端同步的会话快照,可导入继续聊。</p>
</div> </div>
<div class="grid-cards"> <div class="panel-head-actions">
<div class="card"> <button class="glass-btn-sm primary" onclick="newChat()">新建聊天</button>
<div class="card-icon">🔍</div> <button class="glass-btn-sm" onclick="renderSessionsPanel();refreshMemory()">刷新</button>
<div class="card-title">深度研究</div>
<div class="card-desc">给一个主题,自动拆解 → 搜索 → 总结 → 输出报告。</div>
<button class="glass-btn-sm" onclick="startResearch()">开始</button>
</div> </div>
<div class="card">
<div class="card-icon">📄</div>
<div class="card-title">文档问答</div>
<div class="card-desc">粘贴长文档或 URL,就文档内容提问。</div>
<button class="glass-btn-sm" onclick="switchTab('chat');fillPrompt('请粘贴文档内容或 URL,我来帮你解读')">去对话</button>
</div> </div>
<div class="card">
<div class="card-icon">🛠</div>
<div class="card-title">工具调用</div>
<div class="card-desc">浏览器 / 文件 / 终端 — Hermes 的工具集通过后端执行。</div>
<button class="glass-btn-sm" onclick="switchTab('chat');fillPrompt('列出你现在有哪些工具可以用')">查询工具</button>
</div> </div>
<div class="card"> <div class="memory-grid">
<div class="card-icon">🧠</div> <div class="memory-pane">
<div class="card-title">记忆体系</div> <div class="memory-pane-head">本地会话</div>
<div class="card-desc">对话历史沉淀进 SQLite FTS5,未来 skill 可自动提炼。</div> <div class="memory-sessions" id="localSessionsList">加载中…</div>
<button class="glass-btn-sm" onclick="openLog()">查看日志</button> </div>
<div class="memory-pane">
<div class="memory-pane-head">云端会话 · <span id="memorySessCount">0</span></div>
<div class="memory-sessions" id="memorySessions">加载中…</div>
</div> </div>
</div> </div>
</section> </section>
<!-- TAB: 仪表盘 --> <!-- TAB: 工作区 -->
<section class="tab-panel" id="tab-dashboard"> <section class="tab-panel" id="tab-office">
<div class="panel-head"> <div class="panel-head">
<div class="panel-head-row"> <div class="panel-head-row">
<div> <div>
<h2>仪表盘</h2> <h2>工作区</h2>
<p>用量、系统状态、快捷入口和实时日志</p> <p>集中查看运行状态、快捷入口、活动热力图和工作节奏</p>
</div> </div>
<div class="panel-head-actions"> <div class="panel-head-actions">
<button class="glass-btn-sm" onclick="refreshDashboard()"> <button class="glass-btn-sm" onclick="refreshDashboard()">
@@ -1074,7 +1164,7 @@ git push # Gitea kangwan/hermes-glass-ui-personal
<div class="stat-body"> <div class="stat-body">
<div class="stat-label">当前模型</div> <div class="stat-label">当前模型</div>
<div class="stat-value" id="statModel">google/gemini-3.1-pro-preview</div> <div class="stat-value" id="statModel">google/gemini-3.1-pro-preview</div>
<div class="stat-sub" id="statModelSub">Provider 以设置为准</div> <div class="stat-sub" id="statModelSub">Provider 以提供商页为准</div>
</div> </div>
</div> </div>
<div class="stat stat-lg"> <div class="stat stat-lg">
@@ -1166,10 +1256,10 @@ git push # Gitea kangwan/hermes-glass-ui-personal
</div> </div>
</section> </section>
<!-- TAB: 集成 --> <!-- TAB: 网关 -->
<section class="tab-panel" id="tab-integrations"> <section class="tab-panel" id="tab-gateway">
<div class="panel-head"> <div class="panel-head">
<h2>机器人集成</h2> <h2>网关</h2>
<p>统一管理飞书、微信、WhatsApp、Telegram、Discord 等外部消息入口。</p> <p>统一管理飞书、微信、WhatsApp、Telegram、Discord 等外部消息入口。</p>
</div> </div>
@@ -1455,6 +1545,6 @@ git push # Gitea kangwan/hermes-glass-ui-personal
</main> </main>
</div> </div>
<script src="./app.js?v=20260511-settings-ia-v35"></script> <script src="./app.js?v=20260511-modules-ia-v36"></script>
</body> </body>
</html> </html>

View File

@@ -3563,6 +3563,7 @@ a { color: var(--orange-3); text-decoration: none; }
} }
#tab-models, #tab-models,
#tab-providers,
#tab-tools { #tab-tools {
overflow: hidden; overflow: hidden;
} }