@fgbg/mdocs 0.8.12 → 0.8.14
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/agent-skills/changelog/SKILL.md +11 -1
- package/agent-skills/deployment-requirements/SKILL.md +1 -1
- package/agent-skills/getting-started-installation/SKILL.md +85 -2
- package/agent-skills/index.json +1 -1
- package/dist/server/agent/Agent/context-usage.js +71 -0
- package/dist/server/agent/Agent/context-usage.js.map +1 -0
- package/dist/server/agent/Agent/run.js +63 -3
- package/dist/server/agent/Agent/run.js.map +1 -1
- package/dist/server/agent/Agent/session-manager.js +131 -59
- package/dist/server/agent/Agent/session-manager.js.map +1 -1
- package/dist/server/agent/Agent/system-prompt.js +5 -4
- package/dist/server/agent/Agent/system-prompt.js.map +1 -1
- package/dist/server/agent/Agent/tools-account.js +12 -0
- package/dist/server/agent/Agent/tools-account.js.map +1 -0
- package/dist/server/agent/Agent/tools-documents.js +198 -0
- package/dist/server/agent/Agent/tools-documents.js.map +1 -0
- package/dist/server/agent/Agent/tools-domains.js +186 -0
- package/dist/server/agent/Agent/tools-domains.js.map +1 -0
- package/dist/server/agent/Agent/tools-visitors.js +29 -0
- package/dist/server/agent/Agent/tools-visitors.js.map +1 -0
- package/dist/server/agent/Config/config.js +20 -0
- package/dist/server/agent/Config/config.js.map +1 -1
- package/dist/server/db/repositories/agent-model-config.repo.js +19 -4
- package/dist/server/db/repositories/agent-model-config.repo.js.map +1 -1
- package/dist/server/db/repositories/document.repo.js +6 -0
- package/dist/server/db/repositories/document.repo.js.map +1 -1
- package/dist/server/db/schema.js +11 -0
- package/dist/server/db/schema.js.map +1 -1
- package/dist/server/documents/document.service.integration.test.js +94 -1
- package/dist/server/documents/document.service.integration.test.js.map +1 -1
- package/dist/server/documents/document.service.js +108 -2
- package/dist/server/documents/document.service.js.map +1 -1
- package/dist/server/documents/markdown-to-lexical.js +3 -1
- package/dist/server/documents/markdown-to-lexical.js.map +1 -1
- package/dist/server/documents/markdown-to-lexical.test.js +3 -1
- package/dist/server/documents/markdown-to-lexical.test.js.map +1 -1
- package/dist/server/routes/agent.routes.js +53 -3
- package/dist/server/routes/agent.routes.js.map +1 -1
- package/dist/server/routes/documents.routes.js +37 -1
- package/dist/server/routes/documents.routes.js.map +1 -1
- package/dist/server/routes/folders.routes.js +1 -1
- package/dist/server/routes/folders.routes.js.map +1 -1
- package/dist/server/storage/file-store.js +18 -0
- package/dist/server/storage/file-store.js.map +1 -1
- package/dist/web/assets/{MergeView-Da6W6Mwd.js → MergeView-Qhbb3A-J.js} +1 -1
- package/dist/web/assets/{_baseUniq-DIXaUG3P.js → _baseUniq-BPXimQST.js} +1 -1
- package/dist/web/assets/{arc-BdrqKgEt.js → arc-C7LTCKRy.js} +1 -1
- package/dist/web/assets/{architectureDiagram-Q4EWVU46-C7DbabmE.js → architectureDiagram-Q4EWVU46-B-XBFokd.js} +1 -1
- package/dist/web/assets/{blockDiagram-DXYQGD6D-xpzHPtBf.js → blockDiagram-DXYQGD6D-C9_mbHZp.js} +1 -1
- package/dist/web/assets/{c4Diagram-AHTNJAMY-C55XB-0p.js → c4Diagram-AHTNJAMY-CRONB73V.js} +1 -1
- package/dist/web/assets/channel-CrbDKiW1.js +1 -0
- package/dist/web/assets/{chunk-4BX2VUAB-Cw9cGcvA.js → chunk-4BX2VUAB-DBtUbpwD.js} +1 -1
- package/dist/web/assets/{chunk-4TB4RGXK-Bb7TaNsp.js → chunk-4TB4RGXK-BR9W6sJO.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-ClfU0bTR.js → chunk-55IACEB6-DGqID-YO.js} +1 -1
- package/dist/web/assets/{chunk-EDXVE4YY-DEYQg7so.js → chunk-EDXVE4YY-BTZQICED.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-l0uxylnu.js → chunk-FMBD7UC4-C6rTAcxq.js} +1 -1
- package/dist/web/assets/{chunk-OYMX7WX6-CTuAfYj2.js → chunk-OYMX7WX6-DhsxoCd1.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-Cd5BdpCj.js → chunk-QZHKN3VN-jzXtJGFf.js} +1 -1
- package/dist/web/assets/{chunk-YZCP3GAM-C4AFz6HK.js → chunk-YZCP3GAM-DTkwJr8k.js} +1 -1
- package/dist/web/assets/classDiagram-6PBFFD2Q-DEa_ZIiP.js +1 -0
- package/dist/web/assets/classDiagram-v2-HSJHXN6E-DEa_ZIiP.js +1 -0
- package/dist/web/assets/clone-CDBuWAOb.js +1 -0
- package/dist/web/assets/{cose-bilkent-S5V4N54A-DPbGSIxQ.js → cose-bilkent-S5V4N54A-CvRBtkvd.js} +1 -1
- package/dist/web/assets/{dagre-KV5264BT-BAqCvioS.js → dagre-KV5264BT-B-1B_mNA.js} +1 -1
- package/dist/web/assets/{diagram-5BDNPKRD-BVhjoylL.js → diagram-5BDNPKRD-BDr0po1l.js} +1 -1
- package/dist/web/assets/{diagram-G4DWMVQ6-BgdBEUo2.js → diagram-G4DWMVQ6-DTv44hrD.js} +1 -1
- package/dist/web/assets/{diagram-MMDJMWI5-v3XiVR4L.js → diagram-MMDJMWI5-Cs_0nhHd.js} +1 -1
- package/dist/web/assets/{diagram-TYMM5635-Bnlc4JL7.js → diagram-TYMM5635-BBAIB0Sw.js} +1 -1
- package/dist/web/assets/{erDiagram-SMLLAGMA-DZNfy5XO.js → erDiagram-SMLLAGMA-CUN320vy.js} +1 -1
- package/dist/web/assets/{flowDiagram-DWJPFMVM-CyNHaoXD.js → flowDiagram-DWJPFMVM-AJtz-K8W.js} +1 -1
- package/dist/web/assets/{ganttDiagram-T4ZO3ILL-D9XKVK6L.js → ganttDiagram-T4ZO3ILL-C3z7RPjn.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-UUTBAWPF-BsoJR5By.js → gitGraphDiagram-UUTBAWPF-ZebvBuFW.js} +1 -1
- package/dist/web/assets/{graph-C6hZEMMA.js → graph-CZBXW5VB.js} +1 -1
- package/dist/web/assets/{index-BiYwrQhS.js → index-DXTdmGJ-.js} +98 -98
- package/dist/web/assets/index-D_o2MyyT.css +1 -0
- package/dist/web/assets/{infoDiagram-42DDH7IO-EsbSvvwg.js → infoDiagram-42DDH7IO-DIEzS_YO.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-UXIWVN3A-opc9PUAq.js → ishikawaDiagram-UXIWVN3A-CIW8AROq.js} +1 -1
- package/dist/web/assets/{journeyDiagram-VCZTEJTY-CGoqXdQp.js → journeyDiagram-VCZTEJTY-vKh9CIix.js} +1 -1
- package/dist/web/assets/{kanban-definition-6JOO6SKY-BsMSOTEO.js → kanban-definition-6JOO6SKY-DuJSEt-8.js} +1 -1
- package/dist/web/assets/{layout-dka4PN3u.js → layout-BtDnDdeK.js} +1 -1
- package/dist/web/assets/{linear-C7358dWt.js → linear-CpW4XqJH.js} +1 -1
- package/dist/web/assets/{min-CkhEOvqb.js → min-BmlYeGLB.js} +1 -1
- package/dist/web/assets/{mindmap-definition-QFDTVHPH-Cw5Uw5sO.js → mindmap-definition-QFDTVHPH-DREG-tc3.js} +1 -1
- package/dist/web/assets/{pieDiagram-DEJITSTG-D_2NhEs9.js → pieDiagram-DEJITSTG-BxlB2Cgn.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-34T5L4WZ-CCoI5QqP.js → quadrantDiagram-34T5L4WZ-B8EjaHe8.js} +1 -1
- package/dist/web/assets/{requirementDiagram-MS252O5E-DlRuHrpI.js → requirementDiagram-MS252O5E-DwoqRuaP.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-XADWPNL6-CqeHUQiU.js → sankeyDiagram-XADWPNL6-BY149EBc.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-FGHM5R23-bAgSxA1M.js → sequenceDiagram-FGHM5R23-BZn7omfU.js} +1 -1
- package/dist/web/assets/{stateDiagram-FHFEXIEX-BY710eMg.js → stateDiagram-FHFEXIEX-Bm71P8kW.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-QKLJ7IA2-Dl3SCA5p.js +1 -0
- package/dist/web/assets/{timeline-definition-GMOUNBTQ-Dm1cJJzA.js → timeline-definition-GMOUNBTQ-93nA-Ttc.js} +1 -1
- package/dist/web/assets/{vennDiagram-DHZGUBPP-x15pVuxW.js → vennDiagram-DHZGUBPP-SrZiPQFG.js} +1 -1
- package/dist/web/assets/{wardley-RL74JXVD-Baa2vTyp.js → wardley-RL74JXVD-Czl9dszt.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-NUSXRM2D-DD4pCNVv.js → wardleyDiagram-NUSXRM2D-BwAHndxm.js} +1 -1
- package/dist/web/assets/{xychartDiagram-5P7HB3ND-Cj82t7kB.js → xychartDiagram-5P7HB3ND-BTDdpAlH.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/dist/web/assets/channel-R2F1bwis.js +0 -1
- package/dist/web/assets/classDiagram-6PBFFD2Q-D8Aoi_kK.js +0 -1
- package/dist/web/assets/classDiagram-v2-HSJHXN6E-D8Aoi_kK.js +0 -1
- package/dist/web/assets/clone-D3UTWyOI.js +0 -1
- package/dist/web/assets/index-C4tGH1XB.css +0 -1
- package/dist/web/assets/stateDiagram-v2-QKLJ7IA2-BQw6J9EK.js +0 -1
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: changelog
|
|
3
3
|
name: "更新日志"
|
|
4
|
-
description: "-
|
|
4
|
+
description: "- **发布包修复**:`prepack` 现会执行完整 `pnpm build`,避免只升版本号却打出旧 `dist`(此前 0.8.12/0.8.13 缺文档拖拽移动、可拖动助手入口等) - **手册**:补充[小服务器安装](./getting-started/installation.md#小服务器安装低内存--云主机)(Swap、`ENOTEMPTY`、低内存 `npm install "
|
|
5
5
|
keywords: []
|
|
6
6
|
source: changelog.md
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# 更新日志
|
|
10
10
|
|
|
11
|
+
## v0.8.14
|
|
12
|
+
|
|
13
|
+
- **发布包修复**:`prepack` 现会执行完整 `pnpm build`,避免只升版本号却打出旧 `dist`(此前 0.8.12/0.8.13 缺文档拖拽移动、可拖动助手入口等)
|
|
14
|
+
- **手册**:补充[小服务器安装](./getting-started/installation.md#小服务器安装低内存--云主机)(Swap、`ENOTEMPTY`、低内存 `npm install -g`)
|
|
15
|
+
|
|
16
|
+
## v0.8.13
|
|
17
|
+
|
|
18
|
+
- **修复空文档白屏**:空 markdown / Lexical `children: []` 打开时补空段落,避免 editor 全局崩溃
|
|
19
|
+
- **手册**:补充[小服务器安装](./getting-started/installation.md#小服务器安装低内存--云主机)(Swap、`ENOTEMPTY`、低内存 `npm install -g`)
|
|
20
|
+
|
|
11
21
|
## v0.8.12
|
|
12
22
|
|
|
13
23
|
- **文档移动**:侧栏可拖拽 Markdown 文档到文件夹 / 同目录文章旁 / 域根;仅创建者后端成功;`POST /api/documents/:id/move`
|
|
@@ -15,7 +15,7 @@ source: deployment/requirements.md
|
|
|
15
15
|
| 运行时 | Node.js 22+ |
|
|
16
16
|
| 包管理器 | pnpm(推荐) |
|
|
17
17
|
| CPU | 1 核(开发/小团队够用) |
|
|
18
|
-
| 内存 | 512 MB
|
|
18
|
+
| 内存 | 运行约 512 MB 以上;**安装阶段**建议 ≥1G 或配置 Swap(见[小服务器安装](../getting-started/installation.md#小服务器安装低内存--云主机)) |
|
|
19
19
|
| 磁盘 | 视文档量而定 |
|
|
20
20
|
|
|
21
21
|
## 依赖
|
|
@@ -103,15 +103,98 @@ mdocs start
|
|
|
103
103
|
|
|
104
104
|
启动后访问 `http://localhost:4000`(npm 安装)或 `http://localhost:5173`(开发模式),看到访客注册弹窗即表示运行成功。
|
|
105
105
|
|
|
106
|
+
## 小服务器安装(低内存 / 云主机)
|
|
107
|
+
|
|
108
|
+
1 核 / 1G 内存左右的机器(如轻量阿里云)上,`npm install -g` 很容易被内核 **`Killed`(OOM)**,或因半残目录报 `ENOTEMPTY`。按下面做更稳。
|
|
109
|
+
|
|
110
|
+
### 1. 加 Swap(强烈建议)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# 加 2G swap
|
|
114
|
+
fallocate -l 2G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=2048
|
|
115
|
+
chmod 600 /swapfile
|
|
116
|
+
mkswap /swapfile
|
|
117
|
+
swapon /swapfile
|
|
118
|
+
free -h
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
确认 `Swap` 一行不再是 `0B`。重启后若要长期保留,把 `/swapfile swap swap defaults 0 0` 写入 `/etc/fstab`。
|
|
122
|
+
|
|
123
|
+
### 2. 清掉半残的全局目录
|
|
124
|
+
|
|
125
|
+
上次安装被 `Killed` 或报 `ENOTEMPTY` 时必做:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
rm -rf /usr/lib/node_modules/@fgbg
|
|
129
|
+
npm cache clean --force
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 3. 限制内存与并发后再装
|
|
133
|
+
|
|
134
|
+
优先走官方 registry(国内镜像可能尚未同步新版本):
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
NODE_OPTIONS="--max-old-space-size=512" npm install -g @fgbg/mdocs@latest \
|
|
138
|
+
--registry=https://registry.npmjs.org/ \
|
|
139
|
+
--legacy-peer-deps \
|
|
140
|
+
--no-audit \
|
|
141
|
+
--no-fund \
|
|
142
|
+
--maxsockets=1
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
不要用 `npm install -g ... -force`:peer 警告可忽略,`-force` 只会更吃内存。
|
|
146
|
+
|
|
147
|
+
### 4. 离线 tgz(网络差时)
|
|
148
|
+
|
|
149
|
+
在网络较好的机器上打包,再拷到服务器:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# 本机
|
|
153
|
+
npm pack @fgbg/mdocs@latest --registry=https://registry.npmjs.org/
|
|
154
|
+
# 得到 fgbg-mdocs-x.y.z.tgz,scp 到服务器后:
|
|
155
|
+
NODE_OPTIONS="--max-old-space-size=512" npm install -g ./fgbg-mdocs-*.tgz \
|
|
156
|
+
--legacy-peer-deps \
|
|
157
|
+
--no-audit \
|
|
158
|
+
--no-fund \
|
|
159
|
+
--maxsockets=1
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 5. 备选:不装全局,本地 + 软链
|
|
163
|
+
|
|
164
|
+
全局仍 OOM 时,可装到当前目录再链出命令:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
cd /root # 或任意工作目录
|
|
168
|
+
NODE_OPTIONS="--max-old-space-size=512" npm install ./fgbg-mdocs-*.tgz \
|
|
169
|
+
--legacy-peer-deps --no-audit --no-fund --maxsockets=1
|
|
170
|
+
ln -sf "$(pwd)/node_modules/.bin/mdocs" /usr/local/bin/mdocs
|
|
171
|
+
mdocs --help
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
若出现 `Cannot find module '.../typebox/build/index.mjs'`,补装依赖即可:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npm install typebox@1.1.38 --no-audit --no-fund
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 常见报错
|
|
181
|
+
|
|
182
|
+
| 现象 | 原因 | 处理 |
|
|
183
|
+
|------|------|------|
|
|
184
|
+
| 安装过程 `Killed` | OOM | 加 Swap,限制 `--maxsockets=1` |
|
|
185
|
+
| `ENOTEMPTY` rename `@fgbg/mdocs` | 上次半残目录 | `rm -rf /usr/lib/node_modules/@fgbg` 后重装 |
|
|
186
|
+
| `ETARGET` / No matching version | 镜像未同步 | `--registry=https://registry.npmjs.org/` 或改用 tgz |
|
|
187
|
+
| `mdocs: command not found` | 用了非 `-g` 安装 | 用全局安装,或软链 `node_modules/.bin/mdocs` |
|
|
188
|
+
|
|
106
189
|
## 升级
|
|
107
190
|
|
|
108
191
|
通过 npm 安装的版本,直接重新安装即可升级:
|
|
109
192
|
|
|
110
193
|
```bash
|
|
111
|
-
npm install -g @fgbg/mdocs@latest
|
|
194
|
+
npm install -g @fgbg/mdocs@latest --registry=https://registry.npmjs.org/
|
|
112
195
|
```
|
|
113
196
|
|
|
114
|
-
|
|
197
|
+
小服务器升级时建议仍按上面「清半残目录 + 限制并发」步骤执行,升级后重启服务即可。
|
|
115
198
|
|
|
116
199
|
## 下一步
|
|
117
200
|
|
package/agent-skills/index.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
{
|
|
3
3
|
"id": "changelog",
|
|
4
4
|
"name": "更新日志",
|
|
5
|
-
"description": "-
|
|
5
|
+
"description": "- **发布包修复**:`prepack` 现会执行完整 `pnpm build`,避免只升版本号却打出旧 `dist`(此前 0.8.12/0.8.13 缺文档拖拽移动、可拖动助手入口等) - **手册**:补充[小服务器安装](./getting-started/installation.md#小服务器安装低内存--云主机)(Swap、`ENOTEMPTY`、低内存 `npm install ",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"source": "changelog.md"
|
|
8
8
|
},
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { getVisitorAgentConfig, DEFAULT_AGENT_CONTEXT_WINDOW } from "../Config/config.js";
|
|
2
|
+
import { getSkillLoader } from "../Skill/skill-loader.js";
|
|
3
|
+
import { buildSystemPrompt } from "./system-prompt.js";
|
|
4
|
+
import { createSkillTools } from "./tools.js";
|
|
5
|
+
import { createAccountTools } from "./tools-account.js";
|
|
6
|
+
import { AgentSessionManager } from "./session-manager.js";
|
|
7
|
+
/** 粗估 token:CJK 约 1 字 1 token,其余约 4 字符 1 token */
|
|
8
|
+
export function estimateTokens(text) {
|
|
9
|
+
if (!text)
|
|
10
|
+
return 0;
|
|
11
|
+
let cjk = 0;
|
|
12
|
+
let other = 0;
|
|
13
|
+
for (const ch of text) {
|
|
14
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
15
|
+
if ((code >= 0x4e00 && code <= 0x9fff) ||
|
|
16
|
+
(code >= 0x3400 && code <= 0x4dbf) ||
|
|
17
|
+
(code >= 0xf900 && code <= 0xfaff)) {
|
|
18
|
+
cjk += 1;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
other += 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return cjk + Math.ceil(other / 4);
|
|
25
|
+
}
|
|
26
|
+
function estimateToolsTokens(visitorId) {
|
|
27
|
+
const skills = getSkillLoader();
|
|
28
|
+
const tools = skills.isReady()
|
|
29
|
+
? [...createSkillTools(skills), ...createAccountTools(visitorId)]
|
|
30
|
+
: createAccountTools(visitorId);
|
|
31
|
+
let total = 0;
|
|
32
|
+
for (const t of tools) {
|
|
33
|
+
total += estimateTokens(t.name);
|
|
34
|
+
total += estimateTokens(t.description ?? "");
|
|
35
|
+
try {
|
|
36
|
+
total += estimateTokens(JSON.stringify(t.parameters ?? {}));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
/* ignore */
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return total;
|
|
43
|
+
}
|
|
44
|
+
function resolveContextLimit(visitorId) {
|
|
45
|
+
return getVisitorAgentConfig(visitorId)?.contextWindow ?? DEFAULT_AGENT_CONTEXT_WINDOW;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 估算当前 lastOpened session 将占用的 context(system + tools + messages)。
|
|
49
|
+
* limit 来自访客 AI 配置的 contextWindow。
|
|
50
|
+
*/
|
|
51
|
+
export async function getAgentContextUsageForApi(visitorId) {
|
|
52
|
+
const limit = resolveContextLimit(visitorId);
|
|
53
|
+
const systemTokens = estimateTokens(buildSystemPrompt());
|
|
54
|
+
const toolTokens = estimateToolsTokens(visitorId);
|
|
55
|
+
const { messages } = await new AgentSessionManager(visitorId).loadLastOpenedMessages();
|
|
56
|
+
let messageTokens = 0;
|
|
57
|
+
for (const m of messages) {
|
|
58
|
+
messageTokens += estimateTokens(m.content);
|
|
59
|
+
}
|
|
60
|
+
const used = Math.min(limit, systemTokens + toolTokens + messageTokens);
|
|
61
|
+
const percent = limit <= 0 ? 0 : Math.min(100, Math.round((used / limit) * 100));
|
|
62
|
+
return { percent, used, limit };
|
|
63
|
+
}
|
|
64
|
+
/** 合并估算与本轮 provider usage.input(取较大者,避免低估) */
|
|
65
|
+
export function mergeContextUsage(estimated, providerInputTokens) {
|
|
66
|
+
const limit = estimated.limit;
|
|
67
|
+
const used = Math.min(limit, Math.max(estimated.used, Math.max(0, Math.floor(providerInputTokens ?? 0))));
|
|
68
|
+
const percent = limit <= 0 ? 0 : Math.min(100, Math.round((used / limit) * 100));
|
|
69
|
+
return { percent, used, limit };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=context-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-usage.js","sourceRoot":"","sources":["../../../../src/server/agent/Agent/context-usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,10 @@ import { getVisitorAgentConfig, } from "../Config/config.js";
|
|
|
5
5
|
import { getSkillLoader } from "../Skill/skill-loader.js";
|
|
6
6
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
7
7
|
import { createSkillTools } from "./tools.js";
|
|
8
|
+
import { createAccountTools } from "./tools-account.js";
|
|
8
9
|
import { AgentSessionManager, } from "./session-manager.js";
|
|
10
|
+
import { getAgentContextUsageForApi, mergeContextUsage, } from "./context-usage.js";
|
|
11
|
+
export { getAgentContextUsageForApi } from "./context-usage.js";
|
|
9
12
|
export function getAgentStatus(visitorId) {
|
|
10
13
|
const cfg = getVisitorAgentConfig(visitorId);
|
|
11
14
|
const skills = getSkillLoader();
|
|
@@ -35,7 +38,7 @@ export async function runOnboardingChat(params) {
|
|
|
35
38
|
if (!skills.isReady())
|
|
36
39
|
throw new Error("skills_missing");
|
|
37
40
|
const { models, model } = createDeepSeekModel(cfg);
|
|
38
|
-
const tools = createSkillTools(skills);
|
|
41
|
+
const tools = [...createSkillTools(skills), ...createAccountTools(visitorId)];
|
|
39
42
|
const systemPrompt = buildSystemPrompt();
|
|
40
43
|
const sessionManager = new AgentSessionManager(visitorId);
|
|
41
44
|
const { sessionId, messages: history } = await sessionManager.loadLastOpenedMessages();
|
|
@@ -48,7 +51,7 @@ export async function runOnboardingChat(params) {
|
|
|
48
51
|
},
|
|
49
52
|
streamFn: models.streamSimple.bind(models),
|
|
50
53
|
});
|
|
51
|
-
const track = { emittedError: false };
|
|
54
|
+
const track = { emittedError: false, lastUsageInput: 0 };
|
|
52
55
|
const unsub = agent.subscribe(bindAgentEvents({
|
|
53
56
|
sessionId,
|
|
54
57
|
sessionManager,
|
|
@@ -64,6 +67,14 @@ export async function runOnboardingChat(params) {
|
|
|
64
67
|
if (leftover && !track.emittedError) {
|
|
65
68
|
onEvent({ type: "error", message: leftover });
|
|
66
69
|
}
|
|
70
|
+
const estimated = await getAgentContextUsageForApi(visitorId);
|
|
71
|
+
const usage = mergeContextUsage(estimated, track.lastUsageInput);
|
|
72
|
+
onEvent({
|
|
73
|
+
type: "context_usage",
|
|
74
|
+
percent: usage.percent,
|
|
75
|
+
used: usage.used,
|
|
76
|
+
limit: usage.limit,
|
|
77
|
+
});
|
|
67
78
|
onEvent({ type: "done" });
|
|
68
79
|
}
|
|
69
80
|
catch (err) {
|
|
@@ -151,6 +162,9 @@ function bindAgentEvents(opts) {
|
|
|
151
162
|
}
|
|
152
163
|
if (event.message.role === "assistant") {
|
|
153
164
|
const msg = event.message;
|
|
165
|
+
if (typeof msg.usage?.input === "number" && msg.usage.input > 0) {
|
|
166
|
+
track.lastUsageInput = Math.max(track.lastUsageInput, msg.usage.input);
|
|
167
|
+
}
|
|
154
168
|
if ((msg.stopReason === "error" || msg.stopReason === "aborted") &&
|
|
155
169
|
msg.errorMessage) {
|
|
156
170
|
track.emittedError = true;
|
|
@@ -177,6 +191,52 @@ function bindAgentEvents(opts) {
|
|
|
177
191
|
sourcesById.set(source.id, source);
|
|
178
192
|
onEvent({ type: "sources", items: [...sourcesById.values()] });
|
|
179
193
|
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (event.type === "tool_execution_end" && !event.isError) {
|
|
197
|
+
const TREE_MUTATING_TOOLS = new Set([
|
|
198
|
+
"create_document",
|
|
199
|
+
"create_folder",
|
|
200
|
+
"move_document",
|
|
201
|
+
]);
|
|
202
|
+
if (TREE_MUTATING_TOOLS.has(event.toolName)) {
|
|
203
|
+
onEvent({ type: "tree_changed", reason: event.toolName });
|
|
204
|
+
}
|
|
205
|
+
if (event.toolName === "search_documents") {
|
|
206
|
+
const results = event.result?.details?.results ?? [];
|
|
207
|
+
const rows = results
|
|
208
|
+
.map((r) => ({
|
|
209
|
+
documentId: String(r.documentId ?? "").trim(),
|
|
210
|
+
title: String(r.displayName ?? "").trim() || "未命名文档",
|
|
211
|
+
summary: String(r.snippet ?? "").trim(),
|
|
212
|
+
}))
|
|
213
|
+
.filter((r) => r.documentId);
|
|
214
|
+
if (rows.length > 0) {
|
|
215
|
+
onEvent({
|
|
216
|
+
type: "document_table",
|
|
217
|
+
title: `搜索结果(${rows.length})`,
|
|
218
|
+
rows,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (event.toolName === "list_my_documents") {
|
|
224
|
+
const documents = event.result?.details?.documents ?? [];
|
|
225
|
+
const rows = documents
|
|
226
|
+
.map((d) => ({
|
|
227
|
+
documentId: String(d.documentId ?? "").trim(),
|
|
228
|
+
title: String(d.displayName ?? "").trim() || "未命名文档",
|
|
229
|
+
summary: String(d.relativePath ?? "").trim(),
|
|
230
|
+
}))
|
|
231
|
+
.filter((r) => r.documentId);
|
|
232
|
+
if (rows.length > 0) {
|
|
233
|
+
onEvent({
|
|
234
|
+
type: "document_table",
|
|
235
|
+
title: `我的文档(${rows.length})`,
|
|
236
|
+
rows,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
180
240
|
}
|
|
181
241
|
};
|
|
182
242
|
}
|
|
@@ -190,7 +250,7 @@ function createDeepSeekModel(cfg) {
|
|
|
190
250
|
reasoning: false,
|
|
191
251
|
input: ["text"],
|
|
192
252
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
193
|
-
contextWindow:
|
|
253
|
+
contextWindow: cfg.contextWindow,
|
|
194
254
|
maxTokens: 8192,
|
|
195
255
|
compat: {
|
|
196
256
|
supportsDeveloperRole: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../../src/server/agent/Agent/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAmB,MAAM,+BAA+B,CAAC;AACvE,OAAO,EACL,YAAY,EACZ,cAAc,GAEf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mDAAmD,CAAC;AACzF,OAAO,EACL,qBAAqB,GAEtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAwB,MAAM,YAAY,CAAC;AACpE,OAAO,EACL,mBAAmB,GAEpB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../../src/server/agent/Agent/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { getConfig } from "../../config/index.js";
|
|
5
|
+
const SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
5
6
|
function safeString(v) {
|
|
6
7
|
if (typeof v !== "string")
|
|
7
8
|
return null;
|
|
@@ -11,18 +12,24 @@ function safeString(v) {
|
|
|
11
12
|
function nowIso() {
|
|
12
13
|
return new Date().toISOString();
|
|
13
14
|
}
|
|
15
|
+
function isSessionId(id) {
|
|
16
|
+
return SESSION_ID_RE.test(id);
|
|
17
|
+
}
|
|
18
|
+
function titleFromUserContent(content) {
|
|
19
|
+
const oneLine = content.replace(/\s+/g, " ").trim();
|
|
20
|
+
if (!oneLine)
|
|
21
|
+
return "新会话";
|
|
22
|
+
return oneLine.length > 36 ? `${oneLine.slice(0, 36)}…` : oneLine;
|
|
23
|
+
}
|
|
14
24
|
export class AgentSessionManager {
|
|
15
25
|
visitorId;
|
|
16
|
-
tenantDir;
|
|
17
26
|
sessionDir;
|
|
18
27
|
metaPath;
|
|
19
28
|
cachedSessionId = null;
|
|
20
29
|
constructor(visitorId) {
|
|
21
30
|
this.visitorId = visitorId;
|
|
22
31
|
const { dataDir } = getConfig();
|
|
23
|
-
|
|
24
|
-
this.tenantDir = path.join(dataDir, "tenant", visitorId);
|
|
25
|
-
this.sessionDir = path.join(this.tenantDir, "agent", "session");
|
|
32
|
+
this.sessionDir = path.join(dataDir, "tenant", visitorId, "agent", "session");
|
|
26
33
|
this.metaPath = path.join(this.sessionDir, "session.json");
|
|
27
34
|
}
|
|
28
35
|
jsonlPath(sessionId) {
|
|
@@ -36,63 +43,53 @@ export class AgentSessionManager {
|
|
|
36
43
|
const txt = await fs.promises.readFile(this.metaPath, "utf8");
|
|
37
44
|
const parsed = JSON.parse(txt);
|
|
38
45
|
const lastOpenedSessionId = safeString(parsed?.lastOpenedSessionId);
|
|
39
|
-
if (!lastOpenedSessionId)
|
|
46
|
+
if (!lastOpenedSessionId || !isSessionId(lastOpenedSessionId))
|
|
40
47
|
return null;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
48
|
+
const sessions = Array.isArray(parsed?.sessions)
|
|
49
|
+
? parsed.sessions.filter((s) => s && isSessionId(String(s.id ?? "")))
|
|
50
|
+
: [];
|
|
51
|
+
return { lastOpenedSessionId, sessions };
|
|
45
52
|
}
|
|
46
53
|
catch (err) {
|
|
47
|
-
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT")
|
|
54
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
48
55
|
return null;
|
|
56
|
+
}
|
|
49
57
|
return null;
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
async writeMeta(meta) {
|
|
53
|
-
|
|
54
|
-
await fs.promises.writeFile(this.metaPath, txt, "utf8");
|
|
55
|
-
}
|
|
56
|
-
ensureSessionEntry(meta, sessionId) {
|
|
57
|
-
if (!meta.sessions)
|
|
58
|
-
meta.sessions = [];
|
|
59
|
-
const exists = meta.sessions.some((s) => s.id === sessionId);
|
|
60
|
-
if (!exists)
|
|
61
|
-
meta.sessions.push({ id: sessionId, updatedAt: nowIso() });
|
|
62
|
-
meta.sessions = meta.sessions
|
|
63
|
-
.filter((s) => safeString(s?.id) === s.id)
|
|
64
|
-
.map((s) => (s.id === sessionId ? { ...s, updatedAt: s.updatedAt ?? nowIso() } : s));
|
|
61
|
+
await fs.promises.writeFile(this.metaPath, JSON.stringify(meta, null, 2), "utf8");
|
|
65
62
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
77
|
-
await this.writeMeta(created);
|
|
78
|
-
this.cachedSessionId = sessionId;
|
|
79
|
-
return sessionId;
|
|
63
|
+
ensureSessionEntry(meta, sessionId, patch) {
|
|
64
|
+
const idx = meta.sessions.findIndex((s) => s.id === sessionId);
|
|
65
|
+
const now = nowIso();
|
|
66
|
+
if (idx < 0) {
|
|
67
|
+
meta.sessions.push({
|
|
68
|
+
id: sessionId,
|
|
69
|
+
title: patch?.title,
|
|
70
|
+
updatedAt: now,
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
80
73
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
await this.writeMeta(meta);
|
|
88
|
-
this.cachedSessionId = sessionId;
|
|
89
|
-
return sessionId;
|
|
74
|
+
const cur = meta.sessions[idx];
|
|
75
|
+
meta.sessions[idx] = {
|
|
76
|
+
...cur,
|
|
77
|
+
title: patch?.title !== undefined ? patch.title : cur.title,
|
|
78
|
+
updatedAt: patch?.touchUpdatedAt === false ? cur.updatedAt ?? now : now,
|
|
79
|
+
};
|
|
90
80
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
81
|
+
sortSummaries(sessions) {
|
|
82
|
+
return sessions
|
|
83
|
+
.map((s) => ({
|
|
84
|
+
id: s.id,
|
|
85
|
+
title: (s.title?.trim() || "新会话"),
|
|
86
|
+
updatedAt: s.updatedAt ?? nowIso(),
|
|
87
|
+
}))
|
|
88
|
+
.sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0));
|
|
89
|
+
}
|
|
90
|
+
async loadTranscript(sessionId) {
|
|
94
91
|
try {
|
|
95
|
-
const txt = await fs.promises.readFile(jsonlPath, "utf8");
|
|
92
|
+
const txt = await fs.promises.readFile(this.jsonlPath(sessionId), "utf8");
|
|
96
93
|
const lines = txt.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
97
94
|
const messages = [];
|
|
98
95
|
for (const line of lines) {
|
|
@@ -108,40 +105,115 @@ export class AgentSessionManager {
|
|
|
108
105
|
/* ignore invalid line */
|
|
109
106
|
}
|
|
110
107
|
}
|
|
111
|
-
return
|
|
108
|
+
return messages;
|
|
112
109
|
}
|
|
113
110
|
catch (err) {
|
|
114
111
|
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
115
|
-
return
|
|
112
|
+
return [];
|
|
116
113
|
}
|
|
117
|
-
return
|
|
114
|
+
return [];
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
|
-
async
|
|
117
|
+
async ensureLastOpened() {
|
|
118
|
+
await this.ensureSessionDir();
|
|
119
|
+
if (this.cachedSessionId)
|
|
120
|
+
return this.cachedSessionId;
|
|
121
121
|
const meta = await this.readMeta();
|
|
122
122
|
if (!meta) {
|
|
123
|
+
const sessionId = randomUUID();
|
|
123
124
|
await this.writeMeta({
|
|
124
125
|
lastOpenedSessionId: sessionId,
|
|
125
126
|
sessions: [{ id: sessionId, updatedAt: nowIso() }],
|
|
126
127
|
});
|
|
127
|
-
|
|
128
|
+
this.cachedSessionId = sessionId;
|
|
129
|
+
return sessionId;
|
|
128
130
|
}
|
|
131
|
+
this.ensureSessionEntry(meta, meta.lastOpenedSessionId, { touchUpdatedAt: false });
|
|
132
|
+
await this.writeMeta(meta);
|
|
133
|
+
this.cachedSessionId = meta.lastOpenedSessionId;
|
|
134
|
+
return meta.lastOpenedSessionId;
|
|
135
|
+
}
|
|
136
|
+
async listSessions() {
|
|
137
|
+
const lastOpenedSessionId = await this.ensureLastOpened();
|
|
138
|
+
const meta = (await this.readMeta());
|
|
139
|
+
return {
|
|
140
|
+
lastOpenedSessionId,
|
|
141
|
+
sessions: this.sortSummaries(meta.sessions),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async loadLastOpenedMessages() {
|
|
145
|
+
const sessionId = await this.ensureLastOpened();
|
|
146
|
+
return { sessionId, messages: await this.loadTranscript(sessionId) };
|
|
147
|
+
}
|
|
148
|
+
/** 新建空会话并设为 lastOpened */
|
|
149
|
+
async createSession() {
|
|
150
|
+
await this.ensureSessionDir();
|
|
151
|
+
const sessionId = randomUUID();
|
|
152
|
+
const meta = (await this.readMeta()) ?? {
|
|
153
|
+
lastOpenedSessionId: sessionId,
|
|
154
|
+
sessions: [],
|
|
155
|
+
};
|
|
129
156
|
meta.lastOpenedSessionId = sessionId;
|
|
130
|
-
this.ensureSessionEntry(meta, sessionId);
|
|
131
|
-
meta.sessions = meta.sessions?.map((s) => (s.id === sessionId ? { ...s, updatedAt: nowIso() } : s));
|
|
157
|
+
this.ensureSessionEntry(meta, sessionId, { title: "新会话", touchUpdatedAt: true });
|
|
132
158
|
await this.writeMeta(meta);
|
|
159
|
+
this.cachedSessionId = sessionId;
|
|
160
|
+
return { sessionId, messages: [] };
|
|
161
|
+
}
|
|
162
|
+
/** 切换 lastOpened 并返回该会话 transcript */
|
|
163
|
+
async openSession(sessionId) {
|
|
164
|
+
if (!isSessionId(sessionId))
|
|
165
|
+
throw new Error("invalid_session_id");
|
|
166
|
+
await this.ensureSessionDir();
|
|
167
|
+
const meta = await this.readMeta();
|
|
168
|
+
const inMeta = Boolean(meta?.sessions.some((s) => s.id === sessionId));
|
|
169
|
+
const hasFile = fs.existsSync(this.jsonlPath(sessionId));
|
|
170
|
+
if (!inMeta && !hasFile)
|
|
171
|
+
throw new Error("session_not_found");
|
|
172
|
+
const next = meta ?? { lastOpenedSessionId: sessionId, sessions: [] };
|
|
173
|
+
next.lastOpenedSessionId = sessionId;
|
|
174
|
+
this.ensureSessionEntry(next, sessionId, { touchUpdatedAt: true });
|
|
175
|
+
await this.writeMeta(next);
|
|
176
|
+
this.cachedSessionId = sessionId;
|
|
177
|
+
return { sessionId, messages: await this.loadTranscript(sessionId) };
|
|
178
|
+
}
|
|
179
|
+
async touchLastOpened(sessionId, patch) {
|
|
180
|
+
const meta = (await this.readMeta()) ?? {
|
|
181
|
+
lastOpenedSessionId: sessionId,
|
|
182
|
+
sessions: [],
|
|
183
|
+
};
|
|
184
|
+
meta.lastOpenedSessionId = sessionId;
|
|
185
|
+
const existing = meta.sessions.find((s) => s.id === sessionId);
|
|
186
|
+
const needTitle = patch?.titleIfEmpty && !(existing?.title && existing.title.trim() && existing.title !== "新会话");
|
|
187
|
+
this.ensureSessionEntry(meta, sessionId, {
|
|
188
|
+
title: needTitle ? patch.titleIfEmpty : undefined,
|
|
189
|
+
touchUpdatedAt: true,
|
|
190
|
+
});
|
|
191
|
+
await this.writeMeta(meta);
|
|
192
|
+
this.cachedSessionId = sessionId;
|
|
133
193
|
}
|
|
134
194
|
async appendUser(sessionId, content) {
|
|
195
|
+
if (!isSessionId(sessionId))
|
|
196
|
+
throw new Error("invalid_session_id");
|
|
135
197
|
await fs.promises.appendFile(this.jsonlPath(sessionId), JSON.stringify({ role: "user", content, ts: nowIso() }) + "\n", "utf8");
|
|
136
|
-
await this.touchLastOpened(sessionId);
|
|
198
|
+
await this.touchLastOpened(sessionId, { titleIfEmpty: titleFromUserContent(content) });
|
|
137
199
|
}
|
|
138
200
|
async appendAssistant(sessionId, content) {
|
|
201
|
+
if (!isSessionId(sessionId))
|
|
202
|
+
throw new Error("invalid_session_id");
|
|
139
203
|
await fs.promises.appendFile(this.jsonlPath(sessionId), JSON.stringify({ role: "assistant", content, ts: nowIso() }) + "\n", "utf8");
|
|
140
204
|
await this.touchLastOpened(sessionId);
|
|
141
205
|
}
|
|
142
206
|
}
|
|
143
207
|
export async function getAgentSessionForApi(visitorId) {
|
|
144
|
-
|
|
145
|
-
|
|
208
|
+
return new AgentSessionManager(visitorId).loadLastOpenedMessages();
|
|
209
|
+
}
|
|
210
|
+
export async function listAgentSessionsForApi(visitorId) {
|
|
211
|
+
return new AgentSessionManager(visitorId).listSessions();
|
|
212
|
+
}
|
|
213
|
+
export async function createAgentSessionForApi(visitorId) {
|
|
214
|
+
return new AgentSessionManager(visitorId).createSession();
|
|
215
|
+
}
|
|
216
|
+
export async function openAgentSessionForApi(visitorId, sessionId) {
|
|
217
|
+
return new AgentSessionManager(visitorId).openSession(sessionId);
|
|
146
218
|
}
|
|
147
219
|
//# sourceMappingURL=session-manager.js.map
|