@fgbg/mdocs 1.3.31 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/agent-skills/changelog/SKILL.md +31 -0
- package/agent-skills/usage-onboarding-ai/SKILL.md +17 -5
- package/agent-skills/usage-settings/SKILL.md +1 -1
- package/dist/server/agent/Agent/run-manager.js +316 -0
- package/dist/server/agent/Agent/run-manager.js.map +1 -0
- package/dist/server/agent/Agent/run-manager.test.js +122 -0
- package/dist/server/agent/Agent/run-manager.test.js.map +1 -0
- package/dist/server/agent/Agent/run-store.js +141 -0
- package/dist/server/agent/Agent/run-store.js.map +1 -0
- package/dist/server/agent/Agent/run-store.test.js +64 -0
- package/dist/server/agent/Agent/run-store.test.js.map +1 -0
- package/dist/server/agent/Agent/run.js +2 -2
- package/dist/server/agent/Agent/run.js.map +1 -1
- package/dist/server/agent/Agent/system-prompt.js +9 -7
- package/dist/server/agent/Agent/system-prompt.js.map +1 -1
- package/dist/server/agent/Agent/tool-call-ui.js +1 -0
- package/dist/server/agent/Agent/tool-call-ui.js.map +1 -1
- package/dist/server/agent/Agent/tool-ui-effects.js +28 -7
- package/dist/server/agent/Agent/tool-ui-effects.js.map +1 -1
- package/dist/server/agent/Agent/tools-registry.js +2 -2
- package/dist/server/agent/Agent/tools-registry.js.map +1 -1
- package/dist/server/agent/Agent/tools-registry.test.js +2 -1
- package/dist/server/agent/Agent/tools-registry.test.js.map +1 -1
- package/dist/server/agent/Agent/tools-template.js +3 -2
- package/dist/server/agent/Agent/tools-template.js.map +1 -1
- package/dist/server/agent/Agent/tools-thinking.js +33 -0
- package/dist/server/agent/Agent/tools-thinking.js.map +1 -0
- package/dist/server/agent/Agent/tools-user-skills.js +50 -39
- package/dist/server/agent/Agent/tools-user-skills.js.map +1 -1
- package/dist/server/agent/Agent/user-skills.js +11 -5
- package/dist/server/agent/Agent/user-skills.js.map +1 -1
- package/dist/server/agent/Config/config.js +47 -0
- package/dist/server/agent/Config/config.js.map +1 -1
- package/dist/server/agent/Config/config.test.js +29 -0
- package/dist/server/agent/Config/config.test.js.map +1 -1
- package/dist/server/app.js +3 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/db/repositories/agent-model-config.repo.js +6 -4
- package/dist/server/db/repositories/agent-model-config.repo.js.map +1 -1
- package/dist/server/db/repositories/agent-user-skill.repo.js +7 -5
- package/dist/server/db/repositories/agent-user-skill.repo.js.map +1 -1
- package/dist/server/db/repositories/agent-user-skill.repo.test.js +31 -0
- package/dist/server/db/repositories/agent-user-skill.repo.test.js.map +1 -0
- package/dist/server/db/schema.js +20 -0
- package/dist/server/db/schema.js.map +1 -1
- package/dist/server/routes/agent.routes.js +108 -18
- package/dist/server/routes/agent.routes.js.map +1 -1
- package/dist/server/search/document-index-manager.js +51 -8
- package/dist/server/search/document-index-manager.js.map +1 -1
- package/dist/server/search/document-index-manager.test.js +78 -2
- package/dist/server/search/document-index-manager.test.js.map +1 -1
- package/dist/shared/file-type-policy.js +1 -1
- package/dist/shared/file-type-policy.js.map +1 -1
- package/dist/shared/file-type-policy.test.js +3 -2
- package/dist/shared/file-type-policy.test.js.map +1 -1
- package/dist/web/assets/AgentChatPanel-_IV-LAS4.js +42 -0
- package/dist/web/assets/{AiWriteWorkbench-Dq0aCk5c.js → AiWriteWorkbench-DzJAmRHY.js} +3 -3
- package/dist/web/assets/{DiagramEditor-NtNY3sZL-DHC7qgkW.js → DiagramEditor-NtNY3sZL-KWdKFFEL.js} +1 -1
- package/dist/web/assets/{DocChrome-7KIXAHsE.js → DocChrome-CtT7xOl6.js} +4 -4
- package/dist/web/assets/{DocumentEditor-CmIk_VBn.js → DocumentEditor-CrU6HKmU.js} +1 -1
- package/dist/web/assets/{HtmlEditor-REIKOoam.js → HtmlEditor-BBcreXJD.js} +1 -1
- package/dist/web/assets/{MergeView-DCgxAslc.js → MergeView-LV_hny8a.js} +1 -1
- package/dist/web/assets/{PlaygroundPage-Cz9TMo7h.js → PlaygroundPage-Bo9rM8MY.js} +1 -1
- package/dist/web/assets/SettingsPage-D09P0j48.js +21 -0
- package/dist/web/assets/SheetEditor-BPTMQ2s5.css +1 -0
- package/dist/web/assets/SheetEditor-Dyk-jpRL.js +26 -0
- package/dist/web/assets/{Toolbar-ClbpubYi.js → Toolbar-C1HmC2Qc.js} +1 -1
- package/dist/web/assets/{VisitorPickerModal-D4xmZbNY.js → VisitorPickerModal-Cx03Papl.js} +1 -1
- package/dist/web/assets/{check-CuLfkkz7.js → check-BAYRiedE.js} +1 -1
- package/dist/web/assets/{copy-CjAdouag.js → copy-J9XXv57X.js} +1 -1
- package/dist/web/assets/{file-type-policy-C6Nun42Q.js → file-type-policy-aRrzC4LZ.js} +1 -1
- package/dist/web/assets/{index-DAhW-QRN.js → index-Btfv_3-Q.js} +46 -46
- package/dist/web/assets/{italic-cqqPz-_Q.js → italic-BfR6fH81.js} +1 -1
- package/dist/web/assets/{meta2dEngine-BD2XbzaV-CNHknbe2.js → meta2dEngine-BD2XbzaV-MY1Hyyeh.js} +1 -1
- package/dist/web/assets/{plus-DkLh2brQ.js → plus-DvWl4lHk.js} +1 -1
- package/dist/web/assets/{react-Dj2vqPq5.js → react-C7aGt3wN.js} +3 -3
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/AgentChatPanel-e1h0rqbg.js +0 -42
- package/dist/web/assets/SettingsPage-DsLbUPt-.js +0 -21
- package/dist/web/assets/SheetEditor-Bkxxf3ly.js +0 -26
- package/dist/web/assets/SheetEditor-DbT_K_Ia.css +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
面向个人开发者与小团队:产品内 **智能助手 + 帮写**,外部 **CLI / Skills** 接入 Cursor / Claude;SQLite + 本地文件,**无账户**即可协作。
|
|
6
6
|
|
|
7
|
-
[文档站点](https://xuhuafeifei.github.io/mdocs-site/) · [GitHub](https://github.com/xuhuafeifei/mdocs) · [npm `@fgbg/mdocs`](https://www.npmjs.com/package/@fgbg/mdocs) · 当前版本 **1.
|
|
7
|
+
[文档站点](https://xuhuafeifei.github.io/mdocs-site/) · [GitHub](https://github.com/xuhuafeifei/mdocs) · [npm `@fgbg/mdocs`](https://www.npmjs.com/package/@fgbg/mdocs) · 当前版本 **1.4.0**
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<img src="image/site主页.png" alt="mdocs 文档站点首页" width="860" />
|
|
@@ -10,6 +10,37 @@ source: changelog.md
|
|
|
10
10
|
|
|
11
11
|
现行文档里,产品概念「域」已改称 **工作空间**。下面各版本仍按当时的叫法记录。
|
|
12
12
|
|
|
13
|
+
## v1.4.0
|
|
14
|
+
|
|
15
|
+
本版重心:**智能助手 / 帮写可后台跑完**,关面板不再掐断对话;配套超时、重连回放与一批体验修补。
|
|
16
|
+
|
|
17
|
+
### 智能助手 / 帮写(后台运行)
|
|
18
|
+
- **关面板 ≠ 取消**:关闭浮层或退出帮写只断开 SSE 订阅,服务端 run 继续跑;重开后自动回放并接上实时流
|
|
19
|
+
- **显式停止**:点停止、强关(含未完成的选择卡 / Skill 表单)、新建 / 切换会话会真正 `cancel`
|
|
20
|
+
- **单会话单活动 run**:同会话已有进行中的对话时再发会 `409 RUN_ACTIVE`
|
|
21
|
+
- **可配超时**:模型配置高级选项「单轮超时」(默认 300 秒,范围 60–3600);超时自动结束并提示
|
|
22
|
+
- **进程重启**:未完成的 run 标为 `interrupted`(不跨进程续跑)
|
|
23
|
+
|
|
24
|
+
### 私人 Agent Skills
|
|
25
|
+
- 新增工具 **`get_user_skill`**:按名读取完整正文与 `updatedAt`
|
|
26
|
+
- **`update_user_skill`**:改为直接写入(带 `expectedUpdatedAt` 防覆盖),不再弹修改表单卡;新建仍用表单卡
|
|
27
|
+
|
|
28
|
+
### 桌面壳(macOS)
|
|
29
|
+
- 附件下载改为 WKWebView **`on_download`** 落到系统下载目录(不再误开系统浏览器丢 Content-Disposition)
|
|
30
|
+
|
|
31
|
+
### 表格 / 搜索 / 其它
|
|
32
|
+
- 删除工作表前增加确认;页脚「添加工作表」更明显
|
|
33
|
+
- 语义 / FTS 索引覆盖表格等可索引类型(不只 Markdown);补齐缺失 FTS 行
|
|
34
|
+
- 手机侧栏边缘滑动与 FAB 手势冲突再收敛
|
|
35
|
+
|
|
36
|
+
升级:`npm i -g @fgbg/mdocs@1.4.0` 或已安装环境执行 `mdocs update`。
|
|
37
|
+
|
|
38
|
+
## v1.3.32
|
|
39
|
+
|
|
40
|
+
### 智能助手
|
|
41
|
+
- **思考强度**:模型配置可选关 / 低 / 高 / 最大(默认低);工具 `set_thinking_level` 可代改默认配置
|
|
42
|
+
- **写完文档卡**:创建 / 覆写后展示可点开的文档卡与 `/#/doc/<id>` 链接
|
|
43
|
+
|
|
13
44
|
## v1.3.31
|
|
14
45
|
|
|
15
46
|
### 智能助手 / 表格
|
|
@@ -45,9 +45,17 @@ mdocs 内置 **两种 AI 能力模式**,共用设置页里的 DeepSeek 配置
|
|
|
45
45
|
浮层内:
|
|
46
46
|
- 顶部:**+** 新建会话、历史列表、关闭
|
|
47
47
|
- 中间:问候语、快捷提问(如「如何发布文档?」「草稿是什么?」「如何创建工作空间?」)与对话内容
|
|
48
|
-
-
|
|
48
|
+
- 底部输入框:占位「把你的问题告诉我…」;发送中可点 **停止** 真正取消本轮
|
|
49
49
|
- 页脚提示:内容由 AI 生成,仅供参考
|
|
50
50
|
|
|
51
|
+
### 后台运行(v1.4.0+)
|
|
52
|
+
|
|
53
|
+
- **关掉浮层不会中断**正在进行的一轮对话:服务端继续跑,下次打开会回放并接上
|
|
54
|
+
- 需要打断时:点输入区旁的 **停止**,或在有未完成选择卡 / Skill 表单时走「强制关闭」
|
|
55
|
+
- **新建会话 / 切换历史**会取消当前活动 run
|
|
56
|
+
- 单轮最长时长可在 **设置 → AI → 高级选项 → 单轮超时** 调整(默认 300 秒)
|
|
57
|
+
- 服务进程重启后,未完成的一轮会标为已中断,需重新发消息
|
|
58
|
+
|
|
51
59
|
### 可以做什么
|
|
52
60
|
|
|
53
61
|
直接问,例如:
|
|
@@ -96,7 +104,7 @@ mdocs 内置 **两种 AI 能力模式**,共用设置页里的 DeepSeek 配置
|
|
|
96
104
|
| 区域 | 说明 |
|
|
97
105
|
|------|------|
|
|
98
106
|
| **顶栏** | 文档名;**取消**退出;**完成并写回**把已接受内容写回(已有文进本地草稿) |
|
|
99
|
-
| **左侧 · 对话** | 说明要写/改什么;助手可「读取帮写工作稿」再「设置帮写正文」更新提案;**+** /
|
|
107
|
+
| **左侧 · 对话** | 说明要写/改什么;助手可「读取帮写工作稿」再「设置帮写正文」更新提案;**+** / 历史管理会话;与智能助手一样支持**后台跑完**(关掉工作台不掐断,重开可续) |
|
|
100
108
|
| **右侧 · 提案** | 红删绿增的分段 Diff;可 **全部接受 / 全部拒绝**,或逐段操作;也可切 **纯编辑** 手改;底部可在变更间跳转 |
|
|
101
109
|
|
|
102
110
|
### 核心设计:高交互审阅
|
|
@@ -145,6 +153,9 @@ mdocs 内置 **两种 AI 能力模式**,共用设置页里的 DeepSeek 配置
|
|
|
145
153
|
- Provider ID — 自定义标识,不填会自动从域名取
|
|
146
154
|
- API 协议 — OpenAI 兼容 / Anthropic 兼容(URL 含 `anthropic` 会自动识别)
|
|
147
155
|
- 配置备注名、Context Window
|
|
156
|
+
- **单轮超时(秒)** — 关闭面板后仍在后台跑的最长时间(默认 300,范围 60–3600)
|
|
157
|
+
|
|
158
|
+
DeepSeek / 自定义配置都支持 **思考强度**(关 / 低 / 高 / 最大)与上述单轮超时。
|
|
148
159
|
|
|
149
160
|
### 多配置管理
|
|
150
161
|
|
|
@@ -166,7 +177,7 @@ mdocs 内置 **两种 AI 能力模式**,共用设置页里的 DeepSeek 配置
|
|
|
166
177
|
- 名称对本账号唯一,仅英文、数字、下划线
|
|
167
178
|
- 简介可选;正文为 Agent 应遵守的步骤与约束
|
|
168
179
|
- 在智能助手 / 帮写输入区点 **Skill** 多选引用;本轮发送成功后选中标签会清空
|
|
169
|
-
-
|
|
180
|
+
- 也可让助手用工具管理:`list_user_skills` / `get_user_skill`(读全文)/ `create_user_skill`(表单卡)/ `update_user_skill`(直接写入,带版本防覆盖)/ `delete_user_skill`
|
|
170
181
|
|
|
171
182
|
落盘与会话历史只保存 skill **名称引用**,不会把正文全文写进聊天记录;请求时再按名称展开进模型上下文。
|
|
172
183
|
|
|
@@ -185,10 +196,11 @@ mdocs 内置 **两种 AI 能力模式**,共用设置页里的 DeepSeek 配置
|
|
|
185
196
|
## 会话(智能助手)
|
|
186
197
|
|
|
187
198
|
- 同一访客的对话会保存在数据目录 `tenant/<访客ID>/agent/session/` 下,刷新或重开浮层仍可续聊。
|
|
188
|
-
- 浮层标题栏
|
|
199
|
+
- 浮层标题栏 **「+」**:新建空会话并切换过去(若有进行中的后台 run,会先取消)。
|
|
189
200
|
- **历史图标**:按最近更新列出会话;点击即可切换并回放该会话(纯文本;工具 UI 不落盘)。
|
|
190
201
|
- 会话标题取自**第一条用户消息**(过长会截断),不是模型摘要。
|
|
191
|
-
-
|
|
202
|
+
- 后台 run 的事件另存于 `tenant/<访客ID>/agent/runs/`(与会话 transcript 分离),用于关面板后回放。
|
|
203
|
+
- 删除、按天数自动清理尚未提供;置顶 / 手动重命名见产品内历史面板。
|
|
192
204
|
|
|
193
205
|
## 明确不会做(智能助手)
|
|
194
206
|
|
|
@@ -77,7 +77,7 @@ Token / Cookie 丢失时的自助找回;引入登录密码后已边缘化。
|
|
|
77
77
|
| 我的收藏 | [收藏功能](./bookmarks.md) |
|
|
78
78
|
| 我的文章 | [我的文章](./my-documents.md) |
|
|
79
79
|
| 工作空间 / 成员模板 | [受限工作空间成员与名单模板](./domain-members.md) |
|
|
80
|
-
| AI | [智能助手(AI)](./onboarding-ai.md)
|
|
80
|
+
| AI | [智能助手(AI)](./onboarding-ai.md)(含模型 / 思考强度 / 单轮超时与私人 Agent Skills) |
|
|
81
81
|
| 保存与发布 | [草稿与同步](./drafts.md) |
|
|
82
82
|
|
|
83
83
|
---
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 服务端持有的 Agent run:与 SSE 连接解耦。
|
|
3
|
+
* 关闭面板只取消订阅;显式 cancel / 超时才 abort。
|
|
4
|
+
*/
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { useLogger } from "../../logger/logger.js";
|
|
7
|
+
import { getVisitorAgentConfig } from "../Config/config.js";
|
|
8
|
+
import { cancelVisitorChoices } from "./choice-pending.js";
|
|
9
|
+
import { cancelVisitorSkillForms } from "./skill-form-pending.js";
|
|
10
|
+
import { runOnboardingChat } from "./run.js";
|
|
11
|
+
import { AgentSessionManager } from "./session-manager.js";
|
|
12
|
+
import { appendRunEvent, getActiveRunId, listVisitorIdsWithRuns, markStaleRunsInterrupted, readRunEventsAfter, readRunMeta, setActiveRun, writeRunMeta, } from "./run-store.js";
|
|
13
|
+
const log = useLogger("agent-run-manager");
|
|
14
|
+
const live = new Map(); // runId → LiveRun
|
|
15
|
+
let bootstrapped = false;
|
|
16
|
+
export function bootstrapAgentRuns() {
|
|
17
|
+
if (bootstrapped)
|
|
18
|
+
return;
|
|
19
|
+
bootstrapped = true;
|
|
20
|
+
for (const visitorId of listVisitorIdsWithRuns()) {
|
|
21
|
+
try {
|
|
22
|
+
markStaleRunsInterrupted(visitorId);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
log.warn("mark stale runs failed for %s: %s", visitorId, err instanceof Error ? err.message : String(err));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
log.info("agent runs bootstrapped");
|
|
29
|
+
}
|
|
30
|
+
function toPublic(meta) {
|
|
31
|
+
return {
|
|
32
|
+
runId: meta.runId,
|
|
33
|
+
sessionId: meta.sessionId,
|
|
34
|
+
status: meta.status,
|
|
35
|
+
startedAt: meta.startedAt,
|
|
36
|
+
deadlineAt: meta.deadlineAt,
|
|
37
|
+
finishedAt: meta.finishedAt,
|
|
38
|
+
lastSeq: meta.lastSeq,
|
|
39
|
+
abortReason: meta.abortReason,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function isActiveStatus(status) {
|
|
43
|
+
return status === "running" || status === "waiting_user";
|
|
44
|
+
}
|
|
45
|
+
function emitToSubscribers(run, streamEvent, seq) {
|
|
46
|
+
for (const sub of run.subscribers.values()) {
|
|
47
|
+
if (seq <= sub.afterSeq)
|
|
48
|
+
continue;
|
|
49
|
+
try {
|
|
50
|
+
sub.onEvent(streamEvent, seq);
|
|
51
|
+
sub.afterSeq = seq;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
/* ignore subscriber errors */
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function persistAndBroadcast(run, event) {
|
|
59
|
+
const record = appendRunEvent(run.meta.visitorId, run.meta.runId, event);
|
|
60
|
+
run.meta.lastSeq = record.seq;
|
|
61
|
+
emitToSubscribers(run, event, record.seq);
|
|
62
|
+
if (event.type === "choice_card" || event.type === "skill_form_card") {
|
|
63
|
+
updateStatus(run, "waiting_user");
|
|
64
|
+
}
|
|
65
|
+
else if (run.meta.status === "waiting_user" &&
|
|
66
|
+
(event.type === "text_delta" ||
|
|
67
|
+
event.type === "thinking_delta" ||
|
|
68
|
+
event.type === "tool_call" ||
|
|
69
|
+
event.type === "choice_expired" ||
|
|
70
|
+
event.type === "skill_form_expired")) {
|
|
71
|
+
updateStatus(run, "running");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function updateStatus(run, status, reason) {
|
|
75
|
+
if (run.meta.status === status)
|
|
76
|
+
return;
|
|
77
|
+
run.meta.status = status;
|
|
78
|
+
if (!isActiveStatus(status)) {
|
|
79
|
+
run.meta.finishedAt = new Date().toISOString();
|
|
80
|
+
if (reason)
|
|
81
|
+
run.meta.abortReason = reason;
|
|
82
|
+
setActiveRun(run.meta.visitorId, run.meta.sessionId, null);
|
|
83
|
+
if (run.timeoutTimer) {
|
|
84
|
+
clearTimeout(run.timeoutTimer);
|
|
85
|
+
run.timeoutTimer = null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (status === "running" || status === "waiting_user") {
|
|
89
|
+
setActiveRun(run.meta.visitorId, run.meta.sessionId, run.meta.runId);
|
|
90
|
+
}
|
|
91
|
+
writeRunMeta(run.meta);
|
|
92
|
+
const record = appendRunEvent(run.meta.visitorId, run.meta.runId, {
|
|
93
|
+
type: "run_status",
|
|
94
|
+
status,
|
|
95
|
+
reason,
|
|
96
|
+
});
|
|
97
|
+
run.meta.lastSeq = record.seq;
|
|
98
|
+
// run_status 不是前端 AgentStreamEvent;仅用于落盘。终态额外发 done/error。
|
|
99
|
+
}
|
|
100
|
+
export async function resolveSessionForChat(params) {
|
|
101
|
+
const mgr = new AgentSessionManager(params.visitorId, {
|
|
102
|
+
kind: params.mode === "coding" ? "coding" : "normal",
|
|
103
|
+
});
|
|
104
|
+
const { sessionId } = await mgr.loadLastOpenedMessages(params.mode === "coding" ? params.documentId ?? null : undefined);
|
|
105
|
+
return { sessionId };
|
|
106
|
+
}
|
|
107
|
+
export function getActiveRunForSession(visitorId, sessionId) {
|
|
108
|
+
const runId = getActiveRunId(visitorId, sessionId);
|
|
109
|
+
if (!runId)
|
|
110
|
+
return null;
|
|
111
|
+
const liveRun = live.get(runId);
|
|
112
|
+
if (liveRun)
|
|
113
|
+
return toPublic(liveRun.meta);
|
|
114
|
+
const meta = readRunMeta(visitorId, runId);
|
|
115
|
+
if (!meta || !isActiveStatus(meta.status)) {
|
|
116
|
+
setActiveRun(visitorId, sessionId, null);
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
// 磁盘说 active 但内存没有 → 进程重启后应已被标 interrupted;兜底清理
|
|
120
|
+
setActiveRun(visitorId, sessionId, null);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
export function getRunPublic(visitorId, runId) {
|
|
124
|
+
const liveRun = live.get(runId);
|
|
125
|
+
if (liveRun && liveRun.meta.visitorId === visitorId)
|
|
126
|
+
return toPublic(liveRun.meta);
|
|
127
|
+
const meta = readRunMeta(visitorId, runId);
|
|
128
|
+
if (!meta || meta.visitorId !== visitorId)
|
|
129
|
+
return null;
|
|
130
|
+
return toPublic(meta);
|
|
131
|
+
}
|
|
132
|
+
export class AgentRunActiveError extends Error {
|
|
133
|
+
activeRun;
|
|
134
|
+
code = "RUN_ACTIVE";
|
|
135
|
+
constructor(activeRun) {
|
|
136
|
+
super("RUN_ACTIVE");
|
|
137
|
+
this.activeRun = activeRun;
|
|
138
|
+
this.name = "AgentRunActiveError";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function startAgentRun(params) {
|
|
142
|
+
bootstrapAgentRuns();
|
|
143
|
+
const mode = params.mode === "coding" ? "coding" : "normal";
|
|
144
|
+
const { sessionId } = await resolveSessionForChat({
|
|
145
|
+
visitorId: params.visitorId,
|
|
146
|
+
mode,
|
|
147
|
+
documentId: params.documentId,
|
|
148
|
+
});
|
|
149
|
+
const existing = getActiveRunForSession(params.visitorId, sessionId);
|
|
150
|
+
if (existing)
|
|
151
|
+
throw new AgentRunActiveError(existing);
|
|
152
|
+
const cfg = getVisitorAgentConfig(params.visitorId);
|
|
153
|
+
const timeoutSec = cfg?.runTimeoutSeconds ?? 300;
|
|
154
|
+
const startedAt = new Date();
|
|
155
|
+
const deadlineAt = new Date(startedAt.getTime() + timeoutSec * 1000);
|
|
156
|
+
const runId = randomUUID();
|
|
157
|
+
const meta = {
|
|
158
|
+
runId,
|
|
159
|
+
sessionId,
|
|
160
|
+
visitorId: params.visitorId,
|
|
161
|
+
mode,
|
|
162
|
+
documentId: mode === "coding" ? params.documentId ?? null : undefined,
|
|
163
|
+
status: "running",
|
|
164
|
+
startedAt: startedAt.toISOString(),
|
|
165
|
+
deadlineAt: deadlineAt.toISOString(),
|
|
166
|
+
lastSeq: 0,
|
|
167
|
+
message: params.message,
|
|
168
|
+
};
|
|
169
|
+
writeRunMeta(meta);
|
|
170
|
+
setActiveRun(params.visitorId, sessionId, runId);
|
|
171
|
+
const ac = new AbortController();
|
|
172
|
+
const run = {
|
|
173
|
+
meta,
|
|
174
|
+
ac,
|
|
175
|
+
timeoutTimer: null,
|
|
176
|
+
subscribers: new Map(),
|
|
177
|
+
executing: Promise.resolve(),
|
|
178
|
+
};
|
|
179
|
+
live.set(runId, run);
|
|
180
|
+
run.timeoutTimer = setTimeout(() => {
|
|
181
|
+
void finishRun(runId, "timed_out", "run_timeout");
|
|
182
|
+
}, timeoutSec * 1000);
|
|
183
|
+
const startedEvt = {
|
|
184
|
+
type: "run_started",
|
|
185
|
+
runId,
|
|
186
|
+
sessionId,
|
|
187
|
+
deadlineAt: meta.deadlineAt,
|
|
188
|
+
};
|
|
189
|
+
persistAndBroadcast(run, startedEvt);
|
|
190
|
+
run.executing = (async () => {
|
|
191
|
+
try {
|
|
192
|
+
await runOnboardingChat({
|
|
193
|
+
visitorId: params.visitorId,
|
|
194
|
+
message: params.message,
|
|
195
|
+
mode,
|
|
196
|
+
skillNames: params.skillNames,
|
|
197
|
+
documentId: params.documentId,
|
|
198
|
+
workingMarkdown: params.workingMarkdown,
|
|
199
|
+
baseMarkdown: params.baseMarkdown,
|
|
200
|
+
references: params.references,
|
|
201
|
+
signal: ac.signal,
|
|
202
|
+
onEvent: (event) => {
|
|
203
|
+
if (ac.signal.aborted && event.type !== "error")
|
|
204
|
+
return;
|
|
205
|
+
persistAndBroadcast(run, event);
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
if (!ac.signal.aborted && isActiveStatus(run.meta.status)) {
|
|
209
|
+
updateStatus(run, "completed");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
if (ac.signal.aborted) {
|
|
214
|
+
// timed_out / cancelled 已由 finishRun 设置
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
218
|
+
persistAndBroadcast(run, { type: "error", message });
|
|
219
|
+
updateStatus(run, "failed", message);
|
|
220
|
+
persistAndBroadcast(run, { type: "done" });
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
for (const sub of run.subscribers.values()) {
|
|
224
|
+
try {
|
|
225
|
+
sub.onClose?.();
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
/* ignore */
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
run.subscribers.clear();
|
|
232
|
+
live.delete(runId);
|
|
233
|
+
}
|
|
234
|
+
})();
|
|
235
|
+
return { run: toPublic(meta) };
|
|
236
|
+
}
|
|
237
|
+
async function finishRun(runId, status, reason) {
|
|
238
|
+
const run = live.get(runId);
|
|
239
|
+
if (!run || !isActiveStatus(run.meta.status))
|
|
240
|
+
return;
|
|
241
|
+
updateStatus(run, status, reason);
|
|
242
|
+
cancelVisitorChoices(run.meta.visitorId);
|
|
243
|
+
cancelVisitorSkillForms(run.meta.visitorId);
|
|
244
|
+
if (!run.ac.signal.aborted) {
|
|
245
|
+
run.ac.abort();
|
|
246
|
+
}
|
|
247
|
+
if (status === "timed_out") {
|
|
248
|
+
persistAndBroadcast(run, {
|
|
249
|
+
type: "error",
|
|
250
|
+
message: `单轮运行已超时(${reason})`,
|
|
251
|
+
});
|
|
252
|
+
persistAndBroadcast(run, { type: "done" });
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
persistAndBroadcast(run, { type: "done" });
|
|
256
|
+
}
|
|
257
|
+
await run.executing.catch(() => undefined);
|
|
258
|
+
}
|
|
259
|
+
export async function cancelAgentRun(visitorId, runId) {
|
|
260
|
+
const run = live.get(runId);
|
|
261
|
+
if (!run || run.meta.visitorId !== visitorId) {
|
|
262
|
+
const meta = readRunMeta(visitorId, runId);
|
|
263
|
+
return meta && meta.visitorId === visitorId ? toPublic(meta) : null;
|
|
264
|
+
}
|
|
265
|
+
await finishRun(runId, "cancelled", "user_cancel");
|
|
266
|
+
const meta = readRunMeta(visitorId, runId);
|
|
267
|
+
return meta ? toPublic(meta) : null;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* 订阅 run 事件:先回放 afterSeq 之后的流式事件,再接实时。
|
|
271
|
+
* 返回 unsubscribe。
|
|
272
|
+
*/
|
|
273
|
+
export function subscribeAgentRun(params) {
|
|
274
|
+
const afterSeq = Math.max(0, params.afterSeq ?? 0);
|
|
275
|
+
const liveRun = live.get(params.runId);
|
|
276
|
+
const meta = liveRun && liveRun.meta.visitorId === params.visitorId
|
|
277
|
+
? liveRun.meta
|
|
278
|
+
: readRunMeta(params.visitorId, params.runId);
|
|
279
|
+
if (!meta || meta.visitorId !== params.visitorId)
|
|
280
|
+
return null;
|
|
281
|
+
// 回放(跳过 run_status)
|
|
282
|
+
const history = readRunEventsAfter(params.visitorId, params.runId, afterSeq);
|
|
283
|
+
let cursor = afterSeq;
|
|
284
|
+
for (const rec of history) {
|
|
285
|
+
if (rec.event && typeof rec.event === "object" && "type" in rec.event) {
|
|
286
|
+
if (rec.event.type === "run_status") {
|
|
287
|
+
cursor = rec.seq;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
params.onEvent(rec.event, rec.seq);
|
|
291
|
+
cursor = rec.seq;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!liveRun || !isActiveStatus(liveRun.meta.status)) {
|
|
295
|
+
params.onClose?.();
|
|
296
|
+
return {
|
|
297
|
+
unsubscribe: () => undefined,
|
|
298
|
+
run: toPublic(meta),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const subId = randomUUID();
|
|
302
|
+
const sub = {
|
|
303
|
+
id: subId,
|
|
304
|
+
afterSeq: cursor,
|
|
305
|
+
onEvent: params.onEvent,
|
|
306
|
+
onClose: params.onClose,
|
|
307
|
+
};
|
|
308
|
+
liveRun.subscribers.set(subId, sub);
|
|
309
|
+
return {
|
|
310
|
+
run: toPublic(liveRun.meta),
|
|
311
|
+
unsubscribe: () => {
|
|
312
|
+
liveRun.subscribers.delete(subId);
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=run-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-manager.js","sourceRoot":"","sources":["../../../../src/server/agent/Agent/run-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { getConfig } from "../../config/index.js";
|
|
6
|
+
vi.mock("../Config/config.js", async (importOriginal) => {
|
|
7
|
+
const actual = await importOriginal();
|
|
8
|
+
return {
|
|
9
|
+
...actual,
|
|
10
|
+
getVisitorAgentConfig: vi.fn(() => ({
|
|
11
|
+
runTimeoutSeconds: 300,
|
|
12
|
+
})),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
vi.mock("./run.js", () => ({
|
|
16
|
+
runOnboardingChat: vi.fn(),
|
|
17
|
+
}));
|
|
18
|
+
vi.mock("./session-manager.js", () => ({
|
|
19
|
+
AgentSessionManager: class {
|
|
20
|
+
async loadLastOpenedMessages() {
|
|
21
|
+
return { sessionId: "sess-fixed", messages: [] };
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
import { getVisitorAgentConfig } from "../Config/config.js";
|
|
26
|
+
import { runOnboardingChat } from "./run.js";
|
|
27
|
+
import { AgentRunActiveError, cancelAgentRun, getActiveRunForSession, startAgentRun, subscribeAgentRun, } from "./run-manager.js";
|
|
28
|
+
const cleanupDirs = [];
|
|
29
|
+
const mockedRun = vi.mocked(runOnboardingChat);
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
mockedRun.mockReset();
|
|
32
|
+
vi.mocked(getVisitorAgentConfig).mockReturnValue({
|
|
33
|
+
runTimeoutSeconds: 300,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
for (const d of cleanupDirs.splice(0)) {
|
|
38
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
describe("AgentRunManager", () => {
|
|
42
|
+
it("keeps run alive after unsubscribe and allows replay", async () => {
|
|
43
|
+
const visitorId = randomUUID();
|
|
44
|
+
cleanupDirs.push(path.join(getConfig().dataDir, "tenant", visitorId));
|
|
45
|
+
let release;
|
|
46
|
+
const gate = new Promise((r) => {
|
|
47
|
+
release = r;
|
|
48
|
+
});
|
|
49
|
+
mockedRun.mockImplementation(async (opts) => {
|
|
50
|
+
opts.onEvent({ type: "text_delta", text: "hello" });
|
|
51
|
+
await gate;
|
|
52
|
+
opts.onEvent({ type: "done" });
|
|
53
|
+
});
|
|
54
|
+
const { run } = await startAgentRun({
|
|
55
|
+
visitorId,
|
|
56
|
+
message: "hi",
|
|
57
|
+
});
|
|
58
|
+
expect(run.status).toBe("running");
|
|
59
|
+
expect(getActiveRunForSession(visitorId, "sess-fixed")?.runId).toBe(run.runId);
|
|
60
|
+
const seen = [];
|
|
61
|
+
const sub1 = subscribeAgentRun({
|
|
62
|
+
visitorId,
|
|
63
|
+
runId: run.runId,
|
|
64
|
+
afterSeq: 0,
|
|
65
|
+
onEvent: (e) => {
|
|
66
|
+
if (e.type === "text_delta")
|
|
67
|
+
seen.push(e.text);
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
expect(sub1).not.toBeNull();
|
|
71
|
+
expect(seen).toContain("hello");
|
|
72
|
+
sub1.unsubscribe();
|
|
73
|
+
// 仍活动:第二订阅者可回放
|
|
74
|
+
const seen2 = [];
|
|
75
|
+
const sub2 = subscribeAgentRun({
|
|
76
|
+
visitorId,
|
|
77
|
+
runId: run.runId,
|
|
78
|
+
afterSeq: 0,
|
|
79
|
+
onEvent: (e) => {
|
|
80
|
+
if (e.type === "text_delta")
|
|
81
|
+
seen2.push(e.text);
|
|
82
|
+
if (e.type === "run_started")
|
|
83
|
+
seen2.push("started");
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
expect(seen2).toEqual(expect.arrayContaining(["started", "hello"]));
|
|
87
|
+
sub2.unsubscribe();
|
|
88
|
+
release();
|
|
89
|
+
await mockedRun.mock.results[0]?.value;
|
|
90
|
+
expect(getActiveRunForSession(visitorId, "sess-fixed")).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
it("rejects a second run on the same session", async () => {
|
|
93
|
+
const visitorId = randomUUID();
|
|
94
|
+
cleanupDirs.push(path.join(getConfig().dataDir, "tenant", visitorId));
|
|
95
|
+
let release;
|
|
96
|
+
const gate = new Promise((r) => {
|
|
97
|
+
release = r;
|
|
98
|
+
});
|
|
99
|
+
mockedRun.mockImplementation(async () => {
|
|
100
|
+
await gate;
|
|
101
|
+
});
|
|
102
|
+
await startAgentRun({ visitorId, message: "one" });
|
|
103
|
+
await expect(startAgentRun({ visitorId, message: "two" })).rejects.toBeInstanceOf(AgentRunActiveError);
|
|
104
|
+
release();
|
|
105
|
+
await mockedRun.mock.results[0]?.value;
|
|
106
|
+
});
|
|
107
|
+
it("cancel stops the run", async () => {
|
|
108
|
+
const visitorId = randomUUID();
|
|
109
|
+
cleanupDirs.push(path.join(getConfig().dataDir, "tenant", visitorId));
|
|
110
|
+
mockedRun.mockImplementation(async (opts) => {
|
|
111
|
+
await new Promise((resolve) => {
|
|
112
|
+
opts.signal?.addEventListener("abort", () => resolve(), { once: true });
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
const { run } = await startAgentRun({ visitorId, message: "x" });
|
|
116
|
+
const cancelled = await cancelAgentRun(visitorId, run.runId);
|
|
117
|
+
expect(cancelled?.status).toBe("cancelled");
|
|
118
|
+
expect(getActiveRunForSession(visitorId, "sess-fixed")).toBeNull();
|
|
119
|
+
await mockedRun.mock.results[0]?.value;
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=run-manager.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-manager.test.js","sourceRoot":"","sources":["../../../../src/server/agent/Agent/run-manager.test.ts"],"names":[],"mappings":"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"}
|