@dsh-edge/dsh-edge-agent 0.0.0-stage → 0.1.1
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/CHANGELOG.md +142 -0
- package/LICENSE +21 -0
- package/README.md +372 -2
- package/config/edge.example.json +179 -0
- package/config/privacy-policy.json +202 -0
- package/cordis.desktop-safe.patch.yml +129 -0
- package/cordis.patch.yml +382 -0
- package/cordis.yml +374 -0
- package/lib/attestation.d.ts +2 -0
- package/lib/attestation.d.ts.map +1 -0
- package/lib/attestation.js +2 -0
- package/lib/audit-log.d.ts +2 -0
- package/lib/audit-log.d.ts.map +1 -0
- package/lib/audit-log.js +2 -0
- package/lib/client.js +87 -0
- package/lib/cloud-llm-adapter.d.ts +2 -0
- package/lib/cloud-llm-adapter.d.ts.map +1 -0
- package/lib/cloud-llm-adapter.js +2 -0
- package/lib/dsh-adapter/attestation.d.ts +42 -0
- package/lib/dsh-adapter/attestation.d.ts.map +1 -0
- package/lib/dsh-adapter/attestation.js +40 -0
- package/lib/dsh-adapter/audit-log.d.ts +139 -0
- package/lib/dsh-adapter/audit-log.d.ts.map +1 -0
- package/lib/dsh-adapter/audit-log.js +376 -0
- package/lib/dsh-adapter/cloud-egress.d.ts +181 -0
- package/lib/dsh-adapter/cloud-egress.d.ts.map +1 -0
- package/lib/dsh-adapter/cloud-egress.js +257 -0
- package/lib/dsh-adapter/cloud-llm-adapter.d.ts +416 -0
- package/lib/dsh-adapter/cloud-llm-adapter.d.ts.map +1 -0
- package/lib/dsh-adapter/cloud-llm-adapter.js +1472 -0
- package/lib/dsh-adapter/contract.d.ts +80 -0
- package/lib/dsh-adapter/contract.d.ts.map +1 -0
- package/lib/dsh-adapter/contract.js +194 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.d.ts +214 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.d.ts.map +1 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.js +409 -0
- package/lib/dsh-adapter/dsh-edge-agent.d.ts +48 -0
- package/lib/dsh-adapter/dsh-edge-agent.d.ts.map +1 -0
- package/lib/dsh-adapter/dsh-edge-agent.js +64 -0
- package/lib/dsh-adapter/edge-config.d.ts +484 -0
- package/lib/dsh-adapter/edge-config.d.ts.map +1 -0
- package/lib/dsh-adapter/edge-config.js +898 -0
- package/lib/dsh-adapter/edge-context-view.d.ts +184 -0
- package/lib/dsh-adapter/edge-context-view.d.ts.map +1 -0
- package/lib/dsh-adapter/edge-context-view.js +483 -0
- package/lib/dsh-adapter/effective-model.d.ts +85 -0
- package/lib/dsh-adapter/effective-model.d.ts.map +1 -0
- package/lib/dsh-adapter/effective-model.js +171 -0
- package/lib/dsh-adapter/etscli-shell-guard.d.ts +244 -0
- package/lib/dsh-adapter/etscli-shell-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/etscli-shell-guard.js +420 -0
- package/lib/dsh-adapter/etscli-tools.d.ts +112 -0
- package/lib/dsh-adapter/etscli-tools.d.ts.map +1 -0
- package/lib/dsh-adapter/etscli-tools.js +393 -0
- package/lib/dsh-adapter/file-logger.d.ts +294 -0
- package/lib/dsh-adapter/file-logger.d.ts.map +1 -0
- package/lib/dsh-adapter/file-logger.js +564 -0
- package/lib/dsh-adapter/function-call-external.d.ts +357 -0
- package/lib/dsh-adapter/function-call-external.d.ts.map +1 -0
- package/lib/dsh-adapter/function-call-external.js +1005 -0
- package/lib/dsh-adapter/host-egress-guard.d.ts +161 -0
- package/lib/dsh-adapter/host-egress-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/host-egress-guard.js +213 -0
- package/lib/dsh-adapter/host-llm-trace.d.ts +198 -0
- package/lib/dsh-adapter/host-llm-trace.d.ts.map +1 -0
- package/lib/dsh-adapter/host-llm-trace.js +344 -0
- package/lib/dsh-adapter/http-api-routes.d.ts +61 -0
- package/lib/dsh-adapter/http-api-routes.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api-routes.js +79 -0
- package/lib/dsh-adapter/http-api-sessions.d.ts +105 -0
- package/lib/dsh-adapter/http-api-sessions.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api-sessions.js +173 -0
- package/lib/dsh-adapter/http-api.d.ts +221 -0
- package/lib/dsh-adapter/http-api.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api.js +1811 -0
- package/lib/dsh-adapter/index.d.ts +62 -0
- package/lib/dsh-adapter/index.d.ts.map +1 -0
- package/lib/dsh-adapter/index.js +97 -0
- package/lib/dsh-adapter/input-guard-messages.d.ts +56 -0
- package/lib/dsh-adapter/input-guard-messages.d.ts.map +1 -0
- package/lib/dsh-adapter/input-guard-messages.js +136 -0
- package/lib/dsh-adapter/input-guard.d.ts +508 -0
- package/lib/dsh-adapter/input-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/input-guard.js +2174 -0
- package/lib/dsh-adapter/intent-prompts.d.ts +77 -0
- package/lib/dsh-adapter/intent-prompts.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-prompts.js +97 -0
- package/lib/dsh-adapter/intent-router.d.ts +710 -0
- package/lib/dsh-adapter/intent-router.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-router.js +2802 -0
- package/lib/dsh-adapter/intent-signals.d.ts +208 -0
- package/lib/dsh-adapter/intent-signals.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-signals.js +391 -0
- package/lib/dsh-adapter/invariant-install.d.ts +16 -0
- package/lib/dsh-adapter/invariant-install.d.ts.map +1 -0
- package/lib/dsh-adapter/invariant-install.js +15 -0
- package/lib/dsh-adapter/invariant.d.ts +16 -0
- package/lib/dsh-adapter/invariant.d.ts.map +1 -0
- package/lib/dsh-adapter/invariant.js +22 -0
- package/lib/dsh-adapter/llm-adapters.d.ts +19 -0
- package/lib/dsh-adapter/llm-adapters.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-adapters.js +16 -0
- package/lib/dsh-adapter/llm-internal-caller.d.ts +92 -0
- package/lib/dsh-adapter/llm-internal-caller.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-internal-caller.js +100 -0
- package/lib/dsh-adapter/llm-max-turns.d.ts +55 -0
- package/lib/dsh-adapter/llm-max-turns.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-max-turns.js +127 -0
- package/lib/dsh-adapter/llm-trace-store.d.ts +131 -0
- package/lib/dsh-adapter/llm-trace-store.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-trace-store.js +205 -0
- package/lib/dsh-adapter/llm-trace.d.ts +450 -0
- package/lib/dsh-adapter/llm-trace.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-trace.js +463 -0
- package/lib/dsh-adapter/local-llm-adapter.d.ts +141 -0
- package/lib/dsh-adapter/local-llm-adapter.d.ts.map +1 -0
- package/lib/dsh-adapter/local-llm-adapter.js +787 -0
- package/lib/dsh-adapter/local-storage.d.ts +96 -0
- package/lib/dsh-adapter/local-storage.d.ts.map +1 -0
- package/lib/dsh-adapter/local-storage.js +198 -0
- package/lib/dsh-adapter/model-router.d.ts +79 -0
- package/lib/dsh-adapter/model-router.d.ts.map +1 -0
- package/lib/dsh-adapter/model-router.js +107 -0
- package/lib/dsh-adapter/ohttp-gateway.d.ts +49 -0
- package/lib/dsh-adapter/ohttp-gateway.d.ts.map +1 -0
- package/lib/dsh-adapter/ohttp-gateway.js +32 -0
- package/lib/dsh-adapter/openai-sse.d.ts +85 -0
- package/lib/dsh-adapter/openai-sse.d.ts.map +1 -0
- package/lib/dsh-adapter/openai-sse.js +462 -0
- package/lib/dsh-adapter/pii-utils.d.ts +96 -0
- package/lib/dsh-adapter/pii-utils.d.ts.map +1 -0
- package/lib/dsh-adapter/pii-utils.js +324 -0
- package/lib/dsh-adapter/plugin-host.d.ts +38 -0
- package/lib/dsh-adapter/plugin-host.d.ts.map +1 -0
- package/lib/dsh-adapter/plugin-host.js +38 -0
- package/lib/dsh-adapter/plugin.d.ts +46 -0
- package/lib/dsh-adapter/plugin.d.ts.map +1 -0
- package/lib/dsh-adapter/plugin.js +100 -0
- package/lib/dsh-adapter/privacy-policy.d.ts +237 -0
- package/lib/dsh-adapter/privacy-policy.d.ts.map +1 -0
- package/lib/dsh-adapter/privacy-policy.js +391 -0
- package/lib/dsh-adapter/prompt-guard.d.ts +152 -0
- package/lib/dsh-adapter/prompt-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/prompt-guard.js +872 -0
- package/lib/dsh-adapter/realm-skill-bridge.d.ts +365 -0
- package/lib/dsh-adapter/realm-skill-bridge.d.ts.map +1 -0
- package/lib/dsh-adapter/realm-skill-bridge.js +1180 -0
- package/lib/dsh-adapter/sanitizer-pipeline.d.ts +140 -0
- package/lib/dsh-adapter/sanitizer-pipeline.d.ts.map +1 -0
- package/lib/dsh-adapter/sanitizer-pipeline.js +431 -0
- package/lib/dsh-adapter/secure-bridge.d.ts +47 -0
- package/lib/dsh-adapter/secure-bridge.d.ts.map +1 -0
- package/lib/dsh-adapter/secure-bridge.js +35 -0
- package/lib/dsh-adapter/session-attribution.d.ts +70 -0
- package/lib/dsh-adapter/session-attribution.d.ts.map +1 -0
- package/lib/dsh-adapter/session-attribution.js +76 -0
- package/lib/dsh-adapter/sessions.d.ts +86 -0
- package/lib/dsh-adapter/sessions.d.ts.map +1 -0
- package/lib/dsh-adapter/sessions.js +179 -0
- package/lib/dsh-adapter/tcm.d.ts +55 -0
- package/lib/dsh-adapter/tcm.d.ts.map +1 -0
- package/lib/dsh-adapter/tcm.js +94 -0
- package/lib/dsh-adapter/td-bus.d.ts +87 -0
- package/lib/dsh-adapter/td-bus.d.ts.map +1 -0
- package/lib/dsh-adapter/td-bus.js +196 -0
- package/lib/dsh-adapter/tool-runner.d.ts +185 -0
- package/lib/dsh-adapter/tool-runner.d.ts.map +1 -0
- package/lib/dsh-adapter/tool-runner.js +271 -0
- package/lib/dsh-adapter/tools.d.ts +118 -0
- package/lib/dsh-adapter/tools.d.ts.map +1 -0
- package/lib/dsh-adapter/tools.js +292 -0
- package/lib/dsh-adapter/types.d.ts +23 -0
- package/lib/dsh-adapter/types.d.ts.map +1 -0
- package/lib/dsh-adapter/types.js +16 -0
- package/lib/dsh-adapter/voice-input.d.ts +30 -0
- package/lib/dsh-adapter/voice-input.d.ts.map +1 -0
- package/lib/dsh-adapter/voice-input.js +34 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.d.ts +152 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.d.ts.map +1 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.js +289 -0
- package/lib/http-api.d.ts +2 -0
- package/lib/http-api.d.ts.map +1 -0
- package/lib/http-api.js +2 -0
- package/lib/index.d.ts +48 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +49 -0
- package/lib/intent-router.d.ts +2 -0
- package/lib/intent-router.d.ts.map +1 -0
- package/lib/intent-router.js +2 -0
- package/lib/llm-adapters.d.ts +2 -0
- package/lib/llm-adapters.d.ts.map +1 -0
- package/lib/llm-adapters.js +2 -0
- package/lib/local-llm-adapter.d.ts +2 -0
- package/lib/local-llm-adapter.d.ts.map +1 -0
- package/lib/local-llm-adapter.js +2 -0
- package/lib/local-storage.d.ts +2 -0
- package/lib/local-storage.d.ts.map +1 -0
- package/lib/local-storage.js +2 -0
- package/lib/model-router.d.ts +2 -0
- package/lib/model-router.d.ts.map +1 -0
- package/lib/model-router.js +2 -0
- package/lib/ohttp-gateway.d.ts +2 -0
- package/lib/ohttp-gateway.d.ts.map +1 -0
- package/lib/ohttp-gateway.js +2 -0
- package/lib/plugin-host.d.ts +2 -0
- package/lib/plugin-host.d.ts.map +1 -0
- package/lib/plugin-host.js +2 -0
- package/lib/privacy-policy.d.ts +2 -0
- package/lib/privacy-policy.d.ts.map +1 -0
- package/lib/privacy-policy.js +2 -0
- package/lib/prompt-guard.d.ts +2 -0
- package/lib/prompt-guard.d.ts.map +1 -0
- package/lib/prompt-guard.js +2 -0
- package/lib/sanitizer-pipeline.d.ts +2 -0
- package/lib/sanitizer-pipeline.d.ts.map +1 -0
- package/lib/sanitizer-pipeline.js +2 -0
- package/lib/secure-bridge.d.ts +2 -0
- package/lib/secure-bridge.d.ts.map +1 -0
- package/lib/secure-bridge.js +2 -0
- package/lib/sessions.d.ts +2 -0
- package/lib/sessions.d.ts.map +1 -0
- package/lib/sessions.js +2 -0
- package/lib/tcm.d.ts +2 -0
- package/lib/tcm.d.ts.map +1 -0
- package/lib/tcm.js +2 -0
- package/lib/td-bus.d.ts +2 -0
- package/lib/td-bus.d.ts.map +1 -0
- package/lib/td-bus.js +2 -0
- package/lib/tools.d.ts +2 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +2 -0
- package/lib/voice-input.d.ts +2 -0
- package/lib/voice-input.d.ts.map +1 -0
- package/lib/voice-input.js +2 -0
- package/package.json +341 -3
- package/presets/agent-preset-edge.json +50 -0
- package/presets/edge.desktop-safe.patch.yml +503 -0
- package/presets/edge.patch.yml +557 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本文件的**唯一权威**是 [`docs/PROGRESS.md`](docs/PROGRESS.md) —— 那里逐条记着
|
|
4
|
+
每一次改动的根因、实证与裁决(§7.x 全量历史)。本文件是给**使用者**看的:装上之后
|
|
5
|
+
能得到什么、有什么边界、怎么排障。
|
|
6
|
+
|
|
7
|
+
格式:[Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/);
|
|
8
|
+
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## [0.1.1] — 2026-11-10
|
|
13
|
+
|
|
14
|
+
**首次发布到 npm registry。** 506 次提交、2026-08-18 → 2026-11-10。
|
|
15
|
+
|
|
16
|
+
### 加载通道
|
|
17
|
+
|
|
18
|
+
桌面版 DSH App 的 **Plugins → Add plugin** 支持四种输入,本包四条都能装:
|
|
19
|
+
|
|
20
|
+
| 通道 | 输入 | 要构建授权 | PATH 要有 pnpm |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| A 本地目录 | 绝对路径 | 否 | 否 |
|
|
23
|
+
| B GitHub 地址 | `git+ssh://git@github.com/<owner>/<repo>` | 是 | 是 |
|
|
24
|
+
| C npm 包名 | `@dsh-edge/dsh-edge-agent` | 否 | 否 |
|
|
25
|
+
| D tarball 路径 | `…/x.tar.gz` 绝对路径 | 否 | 否 |
|
|
26
|
+
|
|
27
|
+
装完**必须点「立即启用」** —— GUI 不会自动启用,否则启动时不加载、右栏不出现
|
|
28
|
+
Edge tab。装法细节见 [`docs/deploy-github.md`](docs/deploy-github.md) /
|
|
29
|
+
[`docs/deploy-npm.md`](docs/deploy-npm.md) / [`docs/deploy-tarball.md`](docs/deploy-tarball.md)。
|
|
30
|
+
|
|
31
|
+
### 隐私管线(边缘模式)
|
|
32
|
+
|
|
33
|
+
宿主自带四种工作方式之外,本包注册了第五种 —— **「边缘模式」= 标准模式 + 本项目的
|
|
34
|
+
全部功能**。标准模式下本项目的插件**完全不生效**(护栏 hook 挂在宿主的 scope 链上);
|
|
35
|
+
判一个会话有没有走本项目链路,**唯一判据是审计链**。
|
|
36
|
+
|
|
37
|
+
- **输入防御**(prompt-guard)—— 正则 + 频率 + PII 分级 + 可选模型辅助检测;
|
|
38
|
+
- **隐私策略**(privacy-policy)—— 业务危险操作与 PII 护栏,REJECT 永不出网;
|
|
39
|
+
- **意图识别**(intent-router)—— 设备正则 → 决策模型 → LLM 分类器 → 规则层
|
|
40
|
+
四级阶梯(**决策模型那档出厂是关的**,§7.182),实体护栏与云闸两道后处理闸收口;
|
|
41
|
+
- **脱敏**(sanitizer + pii-utils)—— 10 类 PII,可逆占位符,出网前替换、回程还原;
|
|
42
|
+
- **编排层**(input-guard)—— 挂 `agent/pre-step` / `agent/request` / `llm/stream`
|
|
43
|
+
三个 hook,谁都绕不过;
|
|
44
|
+
- **Realm 闸门**—— LOCAL/CLOUD realm 分离,skill 与工具按 realm 放行;
|
|
45
|
+
- **威胁总线**(td-bus)—— 云请求速率 / 连续脱敏失败 / 权限拒绝 / 端点失联
|
|
46
|
+
四档阈值,逐级升到 block。
|
|
47
|
+
|
|
48
|
+
### LLM 路由
|
|
49
|
+
|
|
50
|
+
两个池(`llm.local` / `llm.cloud`)**完全对称**,各三种模式:
|
|
51
|
+
|
|
52
|
+
| `baseUrl` | `delegate` | 行为 |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| 填了 | 任意 | **直连** —— edge 自有池,密钥走 `keyRef` |
|
|
55
|
+
| 留空 | `true`(默认) | **DELEGATE** —— 转发宿主 GUI 里选中的模型,凭据归宿主 |
|
|
56
|
+
| 留空 | `false` | **不注册路由** —— LOCAL 意图 fail-closed |
|
|
57
|
+
|
|
58
|
+
出网口径**不对称**(这是裁决,不是 bug):云池只带「之前上过云的历史」;本地池带
|
|
59
|
+
**全部**历史。出网那一跳由 `host-egress-guard` 兜底裁剪(挂在 `llm/stream`
|
|
60
|
+
waterfall 上)。
|
|
61
|
+
|
|
62
|
+
### 边缘模式的模型路由(fail-closed)
|
|
63
|
+
|
|
64
|
+
宿主 0.2.2-rc.1 起,**路由只由会话级 `modelSelection` 决定** —— 在
|
|
65
|
+
`agent/request` 里改写 provider/model 会被宿主的 `prepareRequest()` 判非法并
|
|
66
|
+
抛错。因此本包:
|
|
67
|
+
|
|
68
|
+
- **边缘模式不改写会话选型**(§7.184):`agent/created` / `agent-preset/selected`
|
|
69
|
+
的钉选与「客户端回写后回钉」全部退役 ⇒ 宿主模型选择器保持主人选的那个;
|
|
70
|
+
- **出网那一跳改道**:本轮判定为 LOCAL、而实际请求的 provider 不是本地 provider
|
|
71
|
+
⇒ 在 `llm/stream` 把它换成 provider = 本地池(**不再拒绝这一轮**);判定为
|
|
72
|
+
CLOUD ⇒ 不改道(用哪个模型是用户自己的行为);改不动 ⇒ fail-closed 抛错;
|
|
73
|
+
- 面板上的「模型」一行显示的是**这一轮真正会跑**的模型与它的真实落点
|
|
74
|
+
(DELEGATE 下 = 委托解析出来的宿主 provider/model),不是配置里的默认值;
|
|
75
|
+
- **宿主模型选择器(输入框下方)里能选到本地池**:本地池在宿主的模型目录里
|
|
76
|
+
**如实声明**它真正会跑的那**一个**模型(直连池 = 池自己的 model id;
|
|
77
|
+
DELEGATE = 委托目标的 model id)—— 宿主会把「空目录的 provider」整组剔出
|
|
78
|
+
目录,不声明就只剩「请选择模型」(§7.180)。**wire 上跑的模型、目录里声明的
|
|
79
|
+
那一项、会话被补钉的 model id 三者是同一个 id**;云池仍然在目录里隐身;
|
|
80
|
+
- **选型不动,实际请求改道**(§7.184):进入边缘模式时插件**一个字都不改**你的
|
|
81
|
+
模型选择(宿主选择器里显示什么就一直是什么);但「本轮必须留在设备上」这条不变式
|
|
82
|
+
由**出网那一跳**保证 —— 本轮判定为 LOCAL 时,请求在 `llm/stream` 被换成
|
|
83
|
+
provider = 本地池(model id 与 wire 同源)。改道改不动(本地路由不存在等)就
|
|
84
|
+
**fail-closed 抛错**,绝不放行到云端。面板的「答案出口」会写明
|
|
85
|
+
**「会话选型 → 实际落点」**,两边都摆出来。
|
|
86
|
+
|
|
87
|
+
npm 包**不带** `config/edge.json` ⇒ 没配本地端点时 `local-llm` 走 DELEGATE
|
|
88
|
+
(转发宿主提供商),但 provider 身份仍是 `local-llm` ⇒ 闸门照常放行、装上即用。
|
|
89
|
+
|
|
90
|
+
### 配置
|
|
91
|
+
|
|
92
|
+
只有**一个文件**:`edge.json`,两个合法落点(§7.182):
|
|
93
|
+
|
|
94
|
+
| 顺位 | 路径 | 适合 |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| 1 | `<插件目录>/config/edge.json` | 「插件装在哪,配置就在哪」—— 打开插件目录就能看到能改的那个 |
|
|
97
|
+
| 2 | `~/.dsh-edge-agent/edge.json` | **推荐** —— 重装 / 删 profile 都活着,npm / git / tarball 三条通道通用 |
|
|
98
|
+
|
|
99
|
+
仓库的 `config/` 目录**不再放活配置**(明文 API key 不该待在 git checkout 里),
|
|
100
|
+
三条通道的产物里也本来就没有这个文件。`config/edge.example.json` 仍随产物发布,
|
|
101
|
+
但它只是**给你抄的模板**(模板里的部署值已并入代码默认值 ⇒ 「没配置文件」与
|
|
102
|
+
「照模板抄」行为一致)。
|
|
103
|
+
|
|
104
|
+
**出厂默认(§7.182 主人裁决)**:三个可选开关默认**关闭** ——
|
|
105
|
+
`jev.enabled`(决策模型是需要本地 `:8001` 服务的可选加速器)、
|
|
106
|
+
`webSearch.tavilyEnabled` / `webSearch.bochaEnabled`
|
|
107
|
+
(⇒ 出厂 web-search 链上只有 DeepSeek 档)。要开就显式写 `true`。
|
|
108
|
+
这三个开关在插件 `apply()` 时读取 ⇒ **改完要重启 App**。
|
|
109
|
+
|
|
110
|
+
`secrets` 段存明文 key,其他段按 `keyRef` 引用它。**`secrets` 与 `llm` 都不是必填**
|
|
111
|
+
—— 桌面版装法下空对象也能跑。
|
|
112
|
+
|
|
113
|
+
### 观测
|
|
114
|
+
|
|
115
|
+
- **审计链** —— 决策级事件,JSONL 落盘,Edge 面板按「意图 / 判定 / 路由决策 /
|
|
116
|
+
LLM 调用」分栏;
|
|
117
|
+
- **ops log** —— 启动与运行期诊断;
|
|
118
|
+
- **Edge 标签页** —— 桌面右栏 4 个面板 + 会话摘要实时可视化;
|
|
119
|
+
- 面板「答案出口」一栏**认得 DELEGATE 出口行**(`llm.*.delegate.*`):直连池与
|
|
120
|
+
DELEGATE 池都会显示「池 + 模型」,不会在明明作答时写「路由未定」(§7.181)。
|
|
121
|
+
LOCAL / CLOUD 的判据仍是**哪个池**(§7.130),DELEGATE 下额外多一行「真实
|
|
122
|
+
落点」,如实说明这一轮转发去了哪儿;
|
|
123
|
+
- **LLM trace** —— 每次往返的请求/响应成对可查(凭据与 PII 脱敏)。
|
|
124
|
+
|
|
125
|
+
### 工程门禁
|
|
126
|
+
|
|
127
|
+
27 道门禁,验的是**产物**不是源码(`npm run build` 一条链跑完):
|
|
128
|
+
构建链完整性、adapter 边界、上游契约、patch 面、preset 隔离、发布面、文档可达性、
|
|
129
|
+
安装包内容、tarball、CI 链不漂移、边缘模式选型回钉(§7.183)……
|
|
130
|
+
|
|
131
|
+
### 已知边界
|
|
132
|
+
|
|
133
|
+
见 [`docs/privacy-policy.md`](docs/privacy-policy.md#已知边界) —— 那里逐条记着
|
|
134
|
+
「当前**未实现或未接线**」的部分(例如标准模式会话不做出网裁剪、本地池 delegate
|
|
135
|
+
模式下内部阶段会跑在宿主模型上)。
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 更早的版本
|
|
140
|
+
|
|
141
|
+
`0.1.0` 及更早只存在于 git 历史里,**从未发布到 registry**。
|
|
142
|
+
逐条实施记录见 [`docs/PROGRESS.md`](docs/PROGRESS.md)。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 chschytzmcy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,373 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-edge-agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> 边缘侧私密智能体 · 基于 DSH (DeepSeek Harness) Cordis 插件机制
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](package.json)
|
|
7
|
+
|
|
8
|
+
`dsh-edge-agent` 是面向用户的端侧私密智能体,以 **bundle 补丁**的形式注册在宿主的
|
|
9
|
+
DSH 桌面版 / CLI 上,新增一个 **Agent Preset —— 边缘模式**:宿主自带的标准 / 创造 /
|
|
10
|
+
PTC / 极简四种工作方式之外,边缘模式 = 标准模式 + 本项目的全部功能(隐私护栏、意图
|
|
11
|
+
判定、路由决策、PII 脱敏、Realm 闸门、审计链、Edge 面板)。默认仍是标准模式;在设置页
|
|
12
|
+
选中「边缘模式」即启用。
|
|
13
|
+
|
|
14
|
+
> 这是**一种工作方式**,不是替代品。护栏 hook 由宿主的 scope 链判定 —— **标准模式下
|
|
15
|
+
> 本项目的插件完全不生效**;Edge tab 常驻可见,但只有边缘模式才真正生效。判一个会话
|
|
16
|
+
> 有没有走本项目链路,**唯一判据是审计链**。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 📥 在 DSH 上加载本插件
|
|
21
|
+
|
|
22
|
+
### 一、加载模型:DSH 怎么认一个 bundle
|
|
23
|
+
|
|
24
|
+
装之前先理解这 5 步 —— 后面所有排障都落在这条链上。
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
① profile 的 package.json → dsh.profile.bundles[] 里追加包名
|
|
28
|
+
└─ 唯一开关:不在这数组里 = 整包不加载(是「没装」,不是「装了没生效」)
|
|
29
|
+
↓
|
|
30
|
+
② 宿主对每个 bundle 读它自己的 package.json → dsh.bundle.patch
|
|
31
|
+
└─ 本包是【数组】,两份一起加载:
|
|
32
|
+
├─ [0] ./cordis.desktop-safe.patch.yml 顶层 patch:宿主行覆盖 + 4 行宿主强制 insert
|
|
33
|
+
└─ [1] ./presets/edge.desktop-safe.patch.yml preset:「边缘模式」工作方式的插件行(独立 scope)
|
|
34
|
+
↓
|
|
35
|
+
③ 插件行的 name 是个子路径 → 查本包 package.json#exports(37 条)→ 定位实现文件
|
|
36
|
+
└─ 例:name: '…/dsh-edge-agent/file-logger' → exports['./file-logger'] → lib/…/file-logger.js
|
|
37
|
+
↓
|
|
38
|
+
④ cordis 按每个插件的 inject 依赖图实例化
|
|
39
|
+
└─ inject 只能加不能减 ⇒ 少点一行 → 插件 ACTIVE 但一条都不拦(见「三层语义」)
|
|
40
|
+
↓
|
|
41
|
+
⑤ 插件通过三种挂法接进 DSH 流程
|
|
42
|
+
├─ 事件钩子 ctx.on('agent/pre-step' | 'llm/stream' | 'tools/pre-execute' | …)
|
|
43
|
+
├─ Service ctx.reflect.provide('promptGuard' | 'auditLog' | …)
|
|
44
|
+
└─ Provider ctx.llm.registerAdapter(['cloud-llm'], …) / ctx.web.registerSearchProvider(…)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
> **两个变体**:`desktop-safe`(上面这个,只 insert、不改写宿主默认)与 `full`
|
|
48
|
+
> (顶层 patch 改写 persona / approval / sandbox / session root 等宿主全局)。
|
|
49
|
+
> **本文所有通道都用 `desktop-safe`** —— 桌面版四通道装的是
|
|
50
|
+
> `dist/installable/dsh-edge-agent-desktop-safe`,独立 profile 走
|
|
51
|
+
> `deploy-profile.sh`(同样是 desktop-safe + 该 profile 自己的 skin overlay)。
|
|
52
|
+
> `full` 变体是给「独立部署一个自有 profile」的场景的。
|
|
53
|
+
|
|
54
|
+
**顶层 patch 为什么只有 4 行 insert**(`dsh-edge-agent` / `file-logger` / `audit-log` /
|
|
55
|
+
`desktop-cookie-janitor`):一行都不是「方便」留下的,是**宿主机制逼的** —— 宿主的
|
|
56
|
+
client-modules 只扫宿主 Loader 的 entries(Edge tab 的浏览器半边只能从这里被发现);
|
|
57
|
+
裸包名行 inject `auditLog` 且 `file-logger` 要先占住 `fileLogger` 服务名 ⇒ 必须在
|
|
58
|
+
root realm;welcome 握手发生在任何工作方式选定之前 ⇒ 修 431 的插件必须在 preset 之外。
|
|
59
|
+
|
|
60
|
+
**三棵树各装什么**:
|
|
61
|
+
|
|
62
|
+
| 树 | 文件 | 装什么 | 跟着谁走 |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| 宿主树顶层 | `cordis.desktop-safe.patch.yml` / `cordis.patch.yml` | 宿主行覆盖 + 4 行强制 insert | 进程级,整机生效 |
|
|
65
|
+
| **preset 子树** | `presets/edge*.patch.yml` | 本项目**行为插件行** | **只跟着「边缘模式」工作方式走** |
|
|
66
|
+
| profile skin | `profiles/<name>/cordis.patch.yml` | 压顶层(headless profile 在这里加 HTTP 监听器) | 跟着那个 profile |
|
|
67
|
+
|
|
68
|
+
⚠️ **preset 子树的 service 对宿主树行不可见**(cordis scope 隔离)—— 顶层 patch 里插一行
|
|
69
|
+
依赖 preset 服务的插件,会永远 pending 并让 boot 硬失败(§7.152 原样)。
|
|
70
|
+
|
|
71
|
+
### 二、桌面版(主路径):Plugins → Add plugin
|
|
72
|
+
|
|
73
|
+
DSH 桌面 App 侧边栏的 **Plugins → Add plugin** 支持四个通道。**装完必须点「立即启用」**
|
|
74
|
+
(GUI 不会自动启用),否则启动时不加载。
|
|
75
|
+
|
|
76
|
+
| 通道 | 输入 | 要构建授权 | PATH 要有 pnpm | 产物形态 | 含真密钥 | 改了会生效吗 |
|
|
77
|
+
|---|---|---|---|---|---|---|
|
|
78
|
+
| **A 本地目录** | 绝对路径 | 否 | 否 | 符号链接 | ⚠️ **有** | ✅ 改文件重启即生效 |
|
|
79
|
+
| **B GitHub 地址** | `git+ssh://git@github.com/<owner>/<repo>` | **是** | **是** | 现场编译 | 否 | ❌ 须重装 |
|
|
80
|
+
| **C npm 包名** | `@dsh-edge/dsh-edge-agent` | 否 | 否 | 预构建 | 否 | 走 `updateBundle` |
|
|
81
|
+
| **D tarball 路径** | `…/x.tar.gz` 绝对路径 | 否 | 否 | pnpm store 冻结副本 | 否 | ❌ 须卸载重装 |
|
|
82
|
+
|
|
83
|
+
四条通道在宿主里是**同一段代码**(`install-spec.ts` 解析四种形态,一律 `pnpm add <spec>`)。
|
|
84
|
+
差别全在本仓的作者侧义务:
|
|
85
|
+
|
|
86
|
+
- **A 本地目录**(本机开发调试)
|
|
87
|
+
```bash
|
|
88
|
+
pnpm install && npm run build:windows # 或 build:macos / build:linux
|
|
89
|
+
```
|
|
90
|
+
在 App 里选「本地目录」,填**绝对路径**指向产物目录:
|
|
91
|
+
```
|
|
92
|
+
Windows C:\Users\<你>\…\dsh-edge-agent\dist\installable\dsh-edge-agent-desktop-safe
|
|
93
|
+
macOS /Users/<你>/…/dsh-edge-agent/dist/installable/dsh-edge-agent-desktop-safe
|
|
94
|
+
Linux /home/<你>/…/dsh-edge-agent/dist/installable/dsh-edge-agent-desktop-safe
|
|
95
|
+
```
|
|
96
|
+
> ⚠️ **相对路径会被拒**(`a local path must be absolute`);Git Bash 的 `/c/Users/…` 是
|
|
97
|
+
> MSYS2 虚拟路径,桌面 App 不认,必须用 `C:\…`。
|
|
98
|
+
> ⚠️ 这个目录**含明文 API key**(`config/edge.json`)⇒ **别把 `dist/` 发给别人**。
|
|
99
|
+
|
|
100
|
+
- **B GitHub 地址**(小范围分发 / 持续迭代)—— 填
|
|
101
|
+
`git+ssh://git@github.com/chschytzmcy/dsh-edge-agent.git`。宿主克隆源码、在本地跑
|
|
102
|
+
`prepare` 编译出 `lib/`、再装成 bundle。首次会失败并提示「允许这些脚本并重试」——
|
|
103
|
+
点它放行;装完点「立即启用」;重启桌面 App。两个前置条件:仓库要能读(私有库建议
|
|
104
|
+
SSH 形式)、**PATH 上要有 `pnpm`**(只有这条通道要,因为 pnpm 给 git 依赖注入的
|
|
105
|
+
`pnpm install` 是用 shell 跑的;宿主自己调 pnpm 用的是 App 自带那支)。
|
|
106
|
+
|
|
107
|
+
- **C npm 包名**(发布给公众)—— 装的是**预构建**包,不需要构建授权、不需要 PATH 上有
|
|
108
|
+
pnpm。⚠️ **包目前仍未发布到 registry**(registry 上查无此包)⇒ 这条通道要等第一次发布之后才成立。
|
|
109
|
+
发布链**本身已经就绪**(`npm run publish:npm` 一条命令走完构建 + 全部门禁 + 发布),
|
|
110
|
+
缺的是**作者侧**:npm 上拥有 `@dsh-edge` scope 的账号/组织 + 发布凭据。
|
|
111
|
+
详见 [`docs/deploy-npm.md`](docs/deploy-npm.md);注册账号与办凭据的逐步操作见
|
|
112
|
+
[`docs/npm-account-setup.md`](docs/npm-account-setup.md)。
|
|
113
|
+
|
|
114
|
+
- **D tarball 路径**(离线分发)——
|
|
115
|
+
```bash
|
|
116
|
+
npm run build:tarball # 编译 + 打包 + 门禁 → dist/dsh-edge-agent-desktop-safe.tar.gz
|
|
117
|
+
```
|
|
118
|
+
在 App 里填 tarball 的**绝对路径**。不需要构建授权、不需要 PATH 上有 pnpm;tarball
|
|
119
|
+
**不含 `config/edge.json`**(目标机器要自己写 `~/.dsh-edge-agent/edge.json`)。
|
|
120
|
+
> ⚠️ **改了 tarball 不会自动生效** —— 装出来是 pnpm store 里的冻结副本,要卸载重装。
|
|
121
|
+
|
|
122
|
+
### 三、独立 HTTP profile(headless CLI)
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm run build:macos -- --deploy-profile=dsh-edge-agent # 或 build:linux
|
|
126
|
+
npm run build:macos -- --deploy-profile=dsh-web # 官方 Web UI (:3080) + IM 机器人
|
|
127
|
+
npm run build:macos -- --deploy-profile=dsh-tui # 终端
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
一条命令做完整件事:build + 装 bundle + 同步配置 + 重启 + 验活。HTTP API 监听 `:21017`
|
|
131
|
+
(`/v1/chat`,GET `/v1/health`)。
|
|
132
|
+
|
|
133
|
+
> ⚠️ **Windows 只有桌面版**:桌面 App 独占 `desktop` profile(`managed exclusively by the
|
|
134
|
+
> Electron application`),CLI 碰不了 ⇒ Windows 上没有独立 headless profile 这条路。
|
|
135
|
+
|
|
136
|
+
### 四、加载后自检:**mount ≠ active ≠ enforced**
|
|
137
|
+
|
|
138
|
+
「装上了」分三层,**boot 不报错只证明前两层**:
|
|
139
|
+
|
|
140
|
+
| 层 | 含义 | 怎么验 |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| **mount** | 插件被挂上 | boot 不报错 |
|
|
143
|
+
| **active** | cordis 把插件置为 ACTIVE | `--dump-config` 里能看到插件行 |
|
|
144
|
+
| **enforced** | 真拦得住 | **审计链里出现执法行**(`blocked.attack` / `blocked.policy` / `etscli.shell.denied` …) |
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# 1. bundle 已注册到 profile?
|
|
148
|
+
grep '@dsh-edge/dsh-edge-agent' ~/.dsh/profiles/<profile>/package.json
|
|
149
|
+
|
|
150
|
+
# 2. 隐私栈挂上了?(期望 ≥4)
|
|
151
|
+
dsh --profile <profile> --dump-config \
|
|
152
|
+
| grep -E 'input-guard|file-logger|intent-router|sanitizer' | wc -l
|
|
153
|
+
|
|
154
|
+
# 3. 桌面版:右栏出现 Edge tab → 发一条本地 query → Intent panel 出现一行
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**排障对照**:
|
|
158
|
+
|
|
159
|
+
| 现象 | 多半是 |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `profile is not mounted` / 插件行根本不在 `--dump-config` | ① 没进 `dsh.profile.bundles` |
|
|
162
|
+
| `1 entry did not activate` + `pending (waiting for services: …)` | ④ inject 闭包不齐 —— 补齐被点名插件的传递依赖 |
|
|
163
|
+
| 插件行在、但会话里什么也不拦 | ③ scope 隔离:preset 子树的 service 对宿主树行不可见 / 当前会话是**标准模式**(不是边缘模式) |
|
|
164
|
+
| 桌面版没有 Edge tab | `lib/client.js` 没构建 —— `prepare` 链里漏了 `build:client`(静默失败,无报错) |
|
|
165
|
+
|
|
166
|
+
### 五、完整文档
|
|
167
|
+
|
|
168
|
+
| 想知道 | 看哪 |
|
|
169
|
+
|---|---|
|
|
170
|
+
| 每个插件挂在哪 / 三种挂法 | [`docs/plugin-mount-points.md`](docs/plugin-mount-points.md) |
|
|
171
|
+
| 装配面全貌 / 三棵树 / 最小必需集 | [`docs/architecture.md`](docs/architecture.md) |
|
|
172
|
+
| 每个文件装了什么 | [`docs/deploy-minimal-files.md`](docs/deploy-minimal-files.md) |
|
|
173
|
+
| 隐私链路的判定表 / 配置清单 | [`docs/privacy-policy.md`](docs/privacy-policy.md) |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## ✨ 核心特性
|
|
178
|
+
|
|
179
|
+
| 能力 | 状态 | 说明 |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| **🔒 端侧优先** | ✅ | 默认本地 LLM 处理;未决轮次一律留在设备上 |
|
|
182
|
+
| **🛡️ 隐私保护** | ✅ | PII 可逆脱敏 + 不可逆日志掩码 + 隐私策略热重载 + HMAC 校验 |
|
|
183
|
+
| **🚦 Realm 三级闸门** | ✅ | LOCAL → SANITIZED → CLOUD;分类→脱敏→路由→执行的完整拦截链,缺件即降级本地 |
|
|
184
|
+
| **🚨 威胁检测** | ✅ | `td-bus` 滑动窗口计数 + 分级 + 隔离;超阈值返回 503 |
|
|
185
|
+
| **📜 审计与可追溯** | ✅ | 追加式 JSONL 审计链 + ops log 详细 LLM request/response 追踪(PII + 密钥双重脱敏) |
|
|
186
|
+
| **🔌 插件化** | ✅ | 插件全部按 subpath 导出(**37 条**),`verify:*` 门禁锁死装配面 |
|
|
187
|
+
| **🌐 零信任通信** | 🚧 | RFC 9458 OHTTP 网关为**桩实现**;当前云端通道走标准 TLS |
|
|
188
|
+
| **🔑 TEE / 远程证明** | 🚧 | `tcm` / `attestation` 为**桩实现**;真实 TEE 后端待接入 |
|
|
189
|
+
|
|
190
|
+
> 当前真正生效的隐私保证来自 **Layer 2**(脱敏管线 + 策略引擎 + 审计链 + Realm 闸门)。
|
|
191
|
+
> 分层状态表见 [`docs/architecture.md`](docs/architecture.md)。
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## ⚙️ 配置
|
|
196
|
+
|
|
197
|
+
配置只有**一个文件**:`edge.json`。所有 profile 读同一份,不依赖环境变量。
|
|
198
|
+
|
|
199
|
+
**两个合法落点**(§7.182):插件装在哪,配置就在哪 —— 但要活过重装,就写 home。
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# 顺位 1:装好后的插件目录里(打开就能看到能改的那个)
|
|
203
|
+
C:\Users\<你>\.dsh\profiles\desktop\node_modules\@dsh-edge\dsh-edge-agent\config\edge.json
|
|
204
|
+
|
|
205
|
+
# 顺位 2:home(跨通道、跨重装、删 profile 都活着)★ 推荐
|
|
206
|
+
C:\Users\<你>\.dsh-edge-agent\edge.json
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
要改配置就抄模板过去填:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
mkdir -p ~/.dsh-edge-agent
|
|
213
|
+
cp config/edge.example.json ~/.dsh-edge-agent/edge.json
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**先说结论:`secrets` 和 `llm` 都不是必填。** 桌面版装法下,`edge.json` 写成
|
|
217
|
+
`{}` 也能正常跑 —— 两个池都会**转发宿主 GUI 里选中的模型**,凭据全归宿主,
|
|
218
|
+
edge 侧一个 secret 都不碰:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
查找顺序(与 profile 无关,代码现状):
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
1. <bundle>/config/edge.json ← 顺位 1:插件目录里那份(用户找得到)
|
|
228
|
+
2. ~/.dsh-edge-agent/edge.json ← 顺位 2:home 那份(重装也活着)★ 推荐
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> **仓库的 `config/` 目录不再放活配置**(§7.182)—— 不再往
|
|
232
|
+
> `<repo>/config/edge.json` 写东西:它含明文 key,且 npm / git / tarball 三条通道的
|
|
233
|
+
> 产物里**本来就没有**这个文件(打包时按设计排除)。仓库那份
|
|
234
|
+
> `config/edge.example.json` 仍然随产物发布,但它只是**给你抄的模板**,不是运行时候选
|
|
235
|
+
> —— 模板里的部署值已并入代码默认值,所以「没配置文件」与「照模板抄」行为一致。
|
|
236
|
+
|
|
237
|
+
### 两个池怎么配
|
|
238
|
+
|
|
239
|
+
`llm.local` 与 `llm.cloud` 现在**完全对称**:
|
|
240
|
+
|
|
241
|
+
| `llm.<池>.baseUrl` | `llm.<池>.delegate` | 行为 |
|
|
242
|
+
|---|---|---|
|
|
243
|
+
| 填了 | 任意 | **直连** —— edge 自有池,密钥走 `keyRef`(端点不鉴权时 `keyRef` 也可以不填)|
|
|
244
|
+
| 留空 | `true`(默认)| **DELEGATE** —— 转发宿主选中的提供商(凭据归宿主,edge 侧不碰 secret)|
|
|
245
|
+
| 留空 | `false` | **不注册路由** —— LOCAL 意图 fail-closed 报错 |
|
|
246
|
+
|
|
247
|
+
也就是说:**不填 `baseUrl` 不等于「没模型」**,而是「用宿主 GUI 里选的那个模型」。
|
|
248
|
+
|
|
249
|
+
```jsonc
|
|
250
|
+
{
|
|
251
|
+
"llm": {
|
|
252
|
+
"local": { "baseUrl": "", "delegate": true }, // → 转发宿主选中的模型
|
|
253
|
+
"cloud": { "baseUrl": "", "delegate": true } // → 同上
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
要 edge 自有直连池时填 `baseUrl + model + keyRef`(`baseUrl` 必须含 `/v1`):
|
|
259
|
+
|
|
260
|
+
```jsonc
|
|
261
|
+
{
|
|
262
|
+
"secrets": { "dashscopeApiKey": "sk-..." },
|
|
263
|
+
"llm": {
|
|
264
|
+
"local": {
|
|
265
|
+
"baseUrl": "https://your-endpoint/v1",
|
|
266
|
+
"model": "your-model",
|
|
267
|
+
"keyRef": "dashscopeApiKey" // 端点不鉴权(本地 llama.cpp / vLLM)时整行可省
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
`delegate: false` 是 kill switch —— 回到「没端点就不注册路由」的旧语义。
|
|
274
|
+
|
|
275
|
+
### 不配会怎样
|
|
276
|
+
|
|
277
|
+
| 你没配的东西 | 结果 |
|
|
278
|
+
|---|---|
|
|
279
|
+
| `llm` 整段 | 两池都 DELEGATE ⇒ **实际是同一个模型**(宿主选的那个)。⚠️ 意图分类与 guard 检测这两个**内部阶段**钉在 `local-llm` 上,端点留空时它们会跑在宿主模型上 ⇒「待检测的用户原文」也会上行(§7.171 的已知边界)|
|
|
280
|
+
| `llm.local.baseUrl` | `local-llm` 路由**照样注册**(转发宿主模型),不是「没模型」|
|
|
281
|
+
| `llm.local.keyRef` | 端点不鉴权 ⇒ 完全正常(不发 Authorization 头);端点要鉴权 ⇒ 401,报错会指名让你配 `keyRef` |
|
|
282
|
+
| `secrets` 整段(**桌面版**)| **完全正常** —— 桌面版不挂 http-api,一个 secret 都不需要 |
|
|
283
|
+
| `secrets.edgeApiKey`(**独立 HTTP profile**)| **必填**:http-api 暴露 `/v1/audit` 与 session delete/fork,没凭据就**拒绝启动**(fail-closed),而不是开一个无鉴权的 API |
|
|
284
|
+
| `secrets.tavilyApiKey` / `bochaApiKey` | 搜索 provider 的 `available()` 变 false ⇒ 被宿主从可用清单里过滤掉(搜索不可用),其余功能照常 |
|
|
285
|
+
|
|
286
|
+
> ⚠️ 两池的**出网口径不同**:云池只带「之前上过云的历史」;本地池带**全部**历史
|
|
287
|
+
> (§7.131 的不对称规则)。本地池走 delegate 时同样按这个口径 —— 已知边界见
|
|
288
|
+
> [`docs/privacy-policy.md`](docs/privacy-policy.md#已知边界)。
|
|
289
|
+
|
|
290
|
+
> ⚠️ `config/edge.json` 含**明文 API key**,绝不能提交到 git(已在 `.gitignore` 里,
|
|
291
|
+
> `.npmignore` 也排除了它 ⇒ 不会进 npm 包)。
|
|
292
|
+
> 完整配置清单(16 类配置项 + 路由行为约定)见
|
|
293
|
+
> [`docs/privacy-policy.md`](docs/privacy-policy.md#配置清单)。
|
|
294
|
+
|
|
295
|
+
### 边缘模式的模型从哪来(选型不动 + 出网那一跳改道)
|
|
296
|
+
|
|
297
|
+
宿主 0.2.2-rc.1 起,**路由只由会话级 `modelSelection` 决定** —— 在
|
|
298
|
+
`agent/request` 里改写 provider/model 会被宿主的 `prepareRequest()` 判非法并抛错。
|
|
299
|
+
§7.184 起,本包**不再改写会话选型**:宿主模型选择器里显示什么,就一直是什么。
|
|
300
|
+
|
|
301
|
+
| 情况 | 结果 |
|
|
302
|
+
|---|---|
|
|
303
|
+
| 进入边缘模式 | **选型一个字都不改**(§7.184)—— 选择器保持主人选的那个,不跳 |
|
|
304
|
+
| 本轮判定为 LOCAL、但会话选的 provider 不是本地 provider | 这一轮在**出网那一跳**被**改道**到本地池(provider 与 model 都换)⇒ 这一轮仍然留在设备上,**不再被拒收**(§7.184) |
|
|
305
|
+
| 本轮判定为 CLOUD | 不改道 —— 本轮就是要出网的(脱敏 + 只带云段历史,§7.131) |
|
|
306
|
+
| 改道改不动(本地路由不存在 / 解析不到本地模型 / 宿主 LLM runtime 不可用) | **fail-closed 抛错**,绝不放行到可能上云的 provider |
|
|
307
|
+
| 宿主模型选择器(输入框下方)里看到什么 | 本地池**如实声明**它真正会跑的那**一个**模型(§7.180)—— 直连池 = 池自己的 model id;DELEGATE = 委托目标的 model id。宿主目录会剔掉「空目录的 provider」,不声明就没有这一项、选择器只能显示「请选择模型」。云池仍在目录里隐身(避免误选 `cloud-llm/deepseek-chat`) |
|
|
308
|
+
|
|
309
|
+
> 判一个会话到底用了哪个 provider,唯一权威是**审计链**:
|
|
310
|
+
> `input-guard.reroute.local`(改道发生,带 `from` = 被改掉的原选型、
|
|
311
|
+
> `to` = 实际落点)/ `input-guard.reroute.blocked`(改不动,fail-closed)。
|
|
312
|
+
>
|
|
313
|
+
> 面板上的「答案出口」一栏会把两边都写出来:**「会话选型 → 实际落点」**
|
|
314
|
+
> (§7.184)—— 选型是你选的那个,实际跑的是本地池那个,两件事都摆出来。
|
|
315
|
+
>
|
|
316
|
+
> 面板上的「模型」一行显示的是**这一轮真正会跑**的模型(DELEGATE 下 = 委托
|
|
317
|
+
> 解析出来的宿主模型与它的落点 provider),不是配置里的默认值 —— 详见
|
|
318
|
+
> [`docs/PROGRESS.md` §7.179](docs/PROGRESS.md)。
|
|
319
|
+
>
|
|
320
|
+
> §7.180 起,**wire 上跑的模型、宿主模型目录里声明的那一项、改道过去的
|
|
321
|
+
> model id** 三者是同一个 id(共用同一份委托目标缓存)⇒ 界面上写的名字不会
|
|
322
|
+
> 跟实际跑的不一致。
|
|
323
|
+
>
|
|
324
|
+
> 面板上的「答案出口」一栏**认得 DELEGATE 出口行**(`llm.*.delegate.*`)——
|
|
325
|
+
> 直连池与 DELEGATE 池都能显示「池 + 模型」,不会在明明作答时写「路由未定」
|
|
326
|
+
> (§7.181)。LOCAL / CLOUD 的判据仍是**哪个池**(§7.130);DELEGATE 下额外多
|
|
327
|
+
> 一行「真实落点」,如实说明这一轮转发去了哪儿。
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## 📚 文档
|
|
332
|
+
|
|
333
|
+
| 文档 | 内容 |
|
|
334
|
+
|---|---|
|
|
335
|
+
| [`docs/README.md`](docs/README.md) | **文档总索引**(每份一句说明 + 状态) |
|
|
336
|
+
| [`docs/architecture.md`](docs/architecture.md) | 架构主文档:四层模型 / Realm / 插件开发 / 威胁模型 |
|
|
337
|
+
| [`docs/plugin-mount-points.md`](docs/plugin-mount-points.md) | 插件挂点表:每个插件的真实挂点 + 三种挂法 |
|
|
338
|
+
| [`docs/privacy-policy.md`](docs/privacy-policy.md) | 隐私链路单一权威文档:策略 + 判定表 + 配置清单 + 审计 + 行为契约 |
|
|
339
|
+
| [`docs/deploy-github.md`](docs/deploy-github.md) | 从 GitHub 仓库地址安装 |
|
|
340
|
+
| [`docs/deploy-npm.md`](docs/deploy-npm.md) | 从 npm 包名安装 + 发布链排障 |
|
|
341
|
+
| [`docs/npm-account-setup.md`](docs/npm-account-setup.md) | 注册 npm 账号/组织(scope `@dsh-edge`)+ 办发布凭据 |
|
|
342
|
+
| [`docs/deploy-tarball.md`](docs/deploy-tarball.md) | 从 tarball 路径安装(离线分发) |
|
|
343
|
+
| [`docs/load-into-local-dsh.md`](docs/load-into-local-dsh.md) | 装进本地 DSH(三条路 + 宿主契约) |
|
|
344
|
+
| [`docs/desktop-troubleshooting.md`](docs/desktop-troubleshooting.md) | 桌面版排障手册 |
|
|
345
|
+
| [`docs/version-policy.md`](docs/version-policy.md) | 上游版本策略 |
|
|
346
|
+
| [`docs/PROGRESS.md`](docs/PROGRESS.md) | 实施进度档案(§7.x 全量历史) |
|
|
347
|
+
|
|
348
|
+
### 开发
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
pnpm install
|
|
352
|
+
pnpm run build # compile → typecheck:client → build:client → build:installable → pack:tarball → verify:build
|
|
353
|
+
pnpm test # 单元测试
|
|
354
|
+
pnpm run lint # eslint
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### 发布到 npm
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
npm run publish:npm # 构建 + 27 道门禁 + dry-run(**默认不发**)
|
|
361
|
+
npm run publish:npm -- --yes # 真发
|
|
362
|
+
npm run publish:npm -- --yes --tag # 发完打 v<version> tag(再 git push origin v<version> 触发 CI)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
前置:`npm login` 一次(凭据写在 `~/.npmrc`;PATH 上要有 `pnpm`)。
|
|
366
|
+
不带 `--yes` **永远不会碰 registry**。详见 [`docs/deploy-npm.md`](docs/deploy-npm.md)。
|
|
367
|
+
|
|
368
|
+
> ⚠️ **`build` 的步骤顺序不能省**:门禁验的是**产物**不是源码 —— `compile` 只重建 Node
|
|
369
|
+
> 半侧,`lib/client.js` 来自 `build:client`(esbuild),`dist/installable/` 来自
|
|
370
|
+
> `build:installable`。缺 `build:client` 的包是**静默**坏法:Edge tab 根本不出现,
|
|
371
|
+
> 没有任何报错。
|
|
372
|
+
|
|
373
|
+
MIT License. 详见 [LICENSE](LICENSE)。
|