@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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §7.179 (2026-11-11): 内部阶段(guard 检测 / 意图分类)**真正会跑**的模型。
|
|
3
|
+
*
|
|
4
|
+
* 背景(主人实测 2026-11-11 12:56,桌面 App):
|
|
5
|
+
*
|
|
6
|
+
* guard / intent 两个阶段的 provider 钉在 `local-llm` 上,但模型名此前只跟
|
|
7
|
+
* `llm.local.model` 走(prompt-guard / intent-router 各自在 apply() 里算一次)。
|
|
8
|
+
* **DELEGATE 模式下 `llm.local.baseUrl` 与 `llm.local.model` 都是空的**
|
|
9
|
+
* (shipped 模板如此,§7.171)⇒ 模型名停在 Schema 默认 `qwen3-4b`,而 wire
|
|
10
|
+
* 上真正跑的是委托解析出来的**宿主当前模型**(实测
|
|
11
|
+
* `qwen-token-plan-cn/deepseek-v4.1-flash`)。
|
|
12
|
+
*
|
|
13
|
+
* ⇒ 面板写「模型 local-llm/qwen3-4b」而实际跑的是另一个模型 = **与实际行为
|
|
14
|
+
* 相反的陈述**(主人的规矩)。§7.176 已经定过同一口径(「DELEGATE 模式下写
|
|
15
|
+
* 宿主当前模型 id —— wire 上真正跑的就是它,不写一个跟实际不符的名字」),
|
|
16
|
+
* 这里把它落到 guard / intent 两个阶段上。
|
|
17
|
+
*
|
|
18
|
+
* 口径(与 local-llm-adapter 的 wire 行为逐条对齐,不是另写一套):
|
|
19
|
+
*
|
|
20
|
+
* - 直连池(`llm.local.baseUrl` 非空):wire body 的 model = `llm.local.model`
|
|
21
|
+
* (adapter 的 `defaultModel`,`options.model` 只是路由键 `route=`)⇒ 取它;
|
|
22
|
+
* - DELEGATE 池(baseUrl 空):wire body 的 model = 委托目标的解析结果
|
|
23
|
+
* (`env` 显式 → `selection` 宿主当前选择 → `probe` 活拓扑探针;探针步
|
|
24
|
+
* 没定 model 时 adapter 在 stream 时取该 provider 的第一个 model id)
|
|
25
|
+
* ⇒ **复用 adapter 的同一份解析链**(`probeCloudTarget`);
|
|
26
|
+
* - 都拿不到 ⇒ 退回调用方给的请求值,并如实标注来处(`requested`)。
|
|
27
|
+
*
|
|
28
|
+
* 单一事实源:`probeCloudTarget`(cloud-llm-adapter 导出)—— local-llm-adapter
|
|
29
|
+
* 的 DELEGATE 分支用的就是它,本模块只复用、不复制解析逻辑。
|
|
30
|
+
*/
|
|
31
|
+
/** 内部阶段真正会跑的模型。 */
|
|
32
|
+
export interface EffectiveLocalModel {
|
|
33
|
+
/** 实际会跑(wire body)的模型 id。 */
|
|
34
|
+
model: string;
|
|
35
|
+
/**
|
|
36
|
+
* 实际落点的 provider。
|
|
37
|
+
*
|
|
38
|
+
* 直连池 = 本地路由自己(`local-llm`);DELEGATE = **宿主 provider**
|
|
39
|
+
* (`qwen-token-plan-cn` 之类)—— 这一项才是「数据往哪儿去」的事实。
|
|
40
|
+
*/
|
|
41
|
+
provider?: string;
|
|
42
|
+
/** 这个 id 的来处(审计与面板的事实标注)。 */
|
|
43
|
+
source: 'local-pool' | 'host-delegate' | 'requested';
|
|
44
|
+
/** 委托解析链的哪一步(与 delegate 审计的 `source` 同名,便于对账)。 */
|
|
45
|
+
via?: 'env' | 'selection' | 'probe';
|
|
46
|
+
}
|
|
47
|
+
/** 本地池里本模块用到的几个字段(测试可直接构造)。 */
|
|
48
|
+
export interface LocalPoolShape {
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
model?: string;
|
|
51
|
+
/** `llm.local.delegate=false` = kill switch(路由不注册)。 */
|
|
52
|
+
delegate?: boolean;
|
|
53
|
+
delegateProvider?: string;
|
|
54
|
+
delegateModel?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* §7.180: **同步**解析(不触网)。
|
|
58
|
+
*
|
|
59
|
+
* 供 input-guard 在 `agent/created` 里**同步**补钉会话选择:宿主的第一轮
|
|
60
|
+
* pre-step 紧跟 agent 创建之后,异步补钉会来不及(§7.179 的 fail-closed
|
|
61
|
+
* 闸门会把第一轮直接拒掉)。
|
|
62
|
+
*
|
|
63
|
+
* 返回 `undefined` = 这一轮解析还需要一次异步的 `firstModelId` 查询(委托
|
|
64
|
+
* 解析链的**探针步**没定 model)⇒ 调用方要么异步补钉,要么明确跳过 ——
|
|
65
|
+
* **不许**拿一个「看起来对」的 id 糊弄过去(§7.176)。
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveEffectiveLocalModelSync(ctx: unknown, pool: LocalPoolShape, requested: string, ownProvider?: string): EffectiveLocalModel | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* 解析「这个内部阶段这一轮真正会跑哪个模型」(池形态由调用方给 ⇒ 可测)。
|
|
70
|
+
*
|
|
71
|
+
* @param ctx 宿主 ctx(读 `llm` / `agentDefaultModel` 两个软依赖)
|
|
72
|
+
* @param pool 本地池现状(直连 / DELEGATE)
|
|
73
|
+
* @param requested 调用方请求的模型 id(拿不到真相时的退路)
|
|
74
|
+
* @param ownProvider 调用方自己那条路由(`local-llm`),用于递归护栏
|
|
75
|
+
*/
|
|
76
|
+
export declare function resolveEffectiveLocalModelWith(ctx: unknown, pool: LocalPoolShape, requested: string, ownProvider?: string): Promise<EffectiveLocalModel>;
|
|
77
|
+
/**
|
|
78
|
+
* 解析「这个内部阶段这一轮真正会跑哪个模型」(池形态取自 config/edge.json)。
|
|
79
|
+
*
|
|
80
|
+
* @param ctx 宿主 ctx(读 `llm` / `agentDefaultModel` 两个软依赖)
|
|
81
|
+
* @param requested 调用方请求的模型 id(拿不到真相时的退路)
|
|
82
|
+
* @param ownProvider 调用方自己那条路由(`local-llm`),用于递归护栏
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveEffectiveLocalModel(ctx: unknown, requested: string, ownProvider?: string): Promise<EffectiveLocalModel>;
|
|
85
|
+
//# sourceMappingURL=effective-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effective-model.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/effective-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAaH,mBAAmB;AACnB,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6BAA6B;IAC7B,MAAM,EAAE,YAAY,GAAG,eAAe,GAAG,WAAW,CAAA;IACpD,kDAAkD;IAClD,GAAG,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,OAAO,CAAA;CACpC;AAED,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AA8DD;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,cAAc,EACpB,SAAS,EAAE,MAAM,EACjB,WAAW,SAAc,GACxB,mBAAmB,GAAG,SAAS,CAoBjC;AAED;;;;;;;GAOG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,cAAc,EACpB,SAAS,EAAE,MAAM,EACjB,WAAW,SAAc,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAoB9B;AAED;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,WAAW,SAAc,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAQ9B"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §7.179 (2026-11-11): 内部阶段(guard 检测 / 意图分类)**真正会跑**的模型。
|
|
3
|
+
*
|
|
4
|
+
* 背景(主人实测 2026-11-11 12:56,桌面 App):
|
|
5
|
+
*
|
|
6
|
+
* guard / intent 两个阶段的 provider 钉在 `local-llm` 上,但模型名此前只跟
|
|
7
|
+
* `llm.local.model` 走(prompt-guard / intent-router 各自在 apply() 里算一次)。
|
|
8
|
+
* **DELEGATE 模式下 `llm.local.baseUrl` 与 `llm.local.model` 都是空的**
|
|
9
|
+
* (shipped 模板如此,§7.171)⇒ 模型名停在 Schema 默认 `qwen3-4b`,而 wire
|
|
10
|
+
* 上真正跑的是委托解析出来的**宿主当前模型**(实测
|
|
11
|
+
* `qwen-token-plan-cn/deepseek-v4.1-flash`)。
|
|
12
|
+
*
|
|
13
|
+
* ⇒ 面板写「模型 local-llm/qwen3-4b」而实际跑的是另一个模型 = **与实际行为
|
|
14
|
+
* 相反的陈述**(主人的规矩)。§7.176 已经定过同一口径(「DELEGATE 模式下写
|
|
15
|
+
* 宿主当前模型 id —— wire 上真正跑的就是它,不写一个跟实际不符的名字」),
|
|
16
|
+
* 这里把它落到 guard / intent 两个阶段上。
|
|
17
|
+
*
|
|
18
|
+
* 口径(与 local-llm-adapter 的 wire 行为逐条对齐,不是另写一套):
|
|
19
|
+
*
|
|
20
|
+
* - 直连池(`llm.local.baseUrl` 非空):wire body 的 model = `llm.local.model`
|
|
21
|
+
* (adapter 的 `defaultModel`,`options.model` 只是路由键 `route=`)⇒ 取它;
|
|
22
|
+
* - DELEGATE 池(baseUrl 空):wire body 的 model = 委托目标的解析结果
|
|
23
|
+
* (`env` 显式 → `selection` 宿主当前选择 → `probe` 活拓扑探针;探针步
|
|
24
|
+
* 没定 model 时 adapter 在 stream 时取该 provider 的第一个 model id)
|
|
25
|
+
* ⇒ **复用 adapter 的同一份解析链**(`probeCloudTarget`);
|
|
26
|
+
* - 都拿不到 ⇒ 退回调用方给的请求值,并如实标注来处(`requested`)。
|
|
27
|
+
*
|
|
28
|
+
* 单一事实源:`probeCloudTarget`(cloud-llm-adapter 导出)—— local-llm-adapter
|
|
29
|
+
* 的 DELEGATE 分支用的就是它,本模块只复用、不复制解析逻辑。
|
|
30
|
+
*/
|
|
31
|
+
import { probeCloudTarget, localPoolTargetCache, } from './cloud-llm-adapter.js';
|
|
32
|
+
import { localPool } from './edge-config.js';
|
|
33
|
+
/** ctx 上取宿主服务的软读法(读不到就是 undefined,不抛)。 */
|
|
34
|
+
function softService(ctx, name) {
|
|
35
|
+
try {
|
|
36
|
+
return ctx?.get?.(name);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 用 adapter 的同一份解析链探一次委托目标。
|
|
44
|
+
*
|
|
45
|
+
* `ownProvider` = 调用方自己那条路由(`local-llm`)—— 递归护栏要排除它自己。
|
|
46
|
+
*
|
|
47
|
+
* §7.180: 结果进**共享**缓存(`localPoolTargetCache`)—— 与 wire
|
|
48
|
+
* (local-llm-adapter 的 DELEGATE 分支)、模型目录(`listModels()`)读的是
|
|
49
|
+
* 同一个目标对象 ⇒ 三处说的必然是同一个模型。
|
|
50
|
+
*/
|
|
51
|
+
function probeTarget(ctx, ownProvider, pool) {
|
|
52
|
+
const llm = softService(ctx, 'llm');
|
|
53
|
+
if (!llm || typeof llm.listProviders !== 'function')
|
|
54
|
+
return null;
|
|
55
|
+
const accessors = {
|
|
56
|
+
llm,
|
|
57
|
+
getAgentDefaultModel: () => softService(ctx, 'agentDefaultModel'),
|
|
58
|
+
};
|
|
59
|
+
const cfg = {
|
|
60
|
+
providers: [ownProvider],
|
|
61
|
+
delegateProvider: pool.delegateProvider,
|
|
62
|
+
delegateModel: pool.delegateModel,
|
|
63
|
+
// `llm.local.delegate=false` = kill switch ⇒ 路由根本没注册 ⇒ 没有委托目标。
|
|
64
|
+
delegateEnabled: pool.delegate,
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
const cached = localPoolTargetCache.get(llm, cfg);
|
|
68
|
+
if (cached !== undefined)
|
|
69
|
+
return cached;
|
|
70
|
+
const target = probeCloudTarget(accessors, cfg);
|
|
71
|
+
localPoolTargetCache.set(llm, cfg, target);
|
|
72
|
+
return target;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** 探针步没定 model 时,adapter 取该 provider 的第一个 model id(同一口径)。 */
|
|
79
|
+
async function firstModelId(ctx, provider) {
|
|
80
|
+
const llm = softService(ctx, 'llm');
|
|
81
|
+
if (!llm || typeof llm.listModels !== 'function')
|
|
82
|
+
return '';
|
|
83
|
+
try {
|
|
84
|
+
const models = await llm.listModels(provider);
|
|
85
|
+
const id = models?.[0]?.id;
|
|
86
|
+
return typeof id === 'string' ? id.trim() : '';
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return '';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* §7.180: **同步**解析(不触网)。
|
|
94
|
+
*
|
|
95
|
+
* 供 input-guard 在 `agent/created` 里**同步**补钉会话选择:宿主的第一轮
|
|
96
|
+
* pre-step 紧跟 agent 创建之后,异步补钉会来不及(§7.179 的 fail-closed
|
|
97
|
+
* 闸门会把第一轮直接拒掉)。
|
|
98
|
+
*
|
|
99
|
+
* 返回 `undefined` = 这一轮解析还需要一次异步的 `firstModelId` 查询(委托
|
|
100
|
+
* 解析链的**探针步**没定 model)⇒ 调用方要么异步补钉,要么明确跳过 ——
|
|
101
|
+
* **不许**拿一个「看起来对」的 id 糊弄过去(§7.176)。
|
|
102
|
+
*/
|
|
103
|
+
export function resolveEffectiveLocalModelSync(ctx, pool, requested, ownProvider = 'local-llm') {
|
|
104
|
+
// 直连池:wire body 的 model 就是池自己的 model(同步可知)。
|
|
105
|
+
if (pool.baseUrl) {
|
|
106
|
+
const model = (pool.model ?? '').trim();
|
|
107
|
+
return {
|
|
108
|
+
model: model.length > 0 ? model : requested,
|
|
109
|
+
provider: ownProvider,
|
|
110
|
+
source: 'local-pool',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// DELEGATE:与 adapter 同一条解析链(同步部分)。
|
|
114
|
+
const target = probeTarget(ctx, ownProvider, pool);
|
|
115
|
+
if (!target) {
|
|
116
|
+
// 解析不到任何落点 ⇒ 池跑不起来;退回请求值并如实标注来处。
|
|
117
|
+
return { model: requested, source: 'requested' };
|
|
118
|
+
}
|
|
119
|
+
const model = (target.model ?? '').trim();
|
|
120
|
+
if (model.length === 0)
|
|
121
|
+
return undefined;
|
|
122
|
+
return { model, provider: target.provider, source: 'host-delegate', via: target.source };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 解析「这个内部阶段这一轮真正会跑哪个模型」(池形态由调用方给 ⇒ 可测)。
|
|
126
|
+
*
|
|
127
|
+
* @param ctx 宿主 ctx(读 `llm` / `agentDefaultModel` 两个软依赖)
|
|
128
|
+
* @param pool 本地池现状(直连 / DELEGATE)
|
|
129
|
+
* @param requested 调用方请求的模型 id(拿不到真相时的退路)
|
|
130
|
+
* @param ownProvider 调用方自己那条路由(`local-llm`),用于递归护栏
|
|
131
|
+
*/
|
|
132
|
+
export async function resolveEffectiveLocalModelWith(ctx, pool, requested, ownProvider = 'local-llm') {
|
|
133
|
+
// 直连池 / 解析不到落点:同步就能定。
|
|
134
|
+
const sync = resolveEffectiveLocalModelSync(ctx, pool, requested, ownProvider);
|
|
135
|
+
if (sync !== undefined && sync.source !== 'host-delegate')
|
|
136
|
+
return sync;
|
|
137
|
+
// DELEGATE 且探针步没定 model:需要一次 firstModelId 查询(异步)。
|
|
138
|
+
const target = probeTarget(ctx, ownProvider, pool);
|
|
139
|
+
if (!target)
|
|
140
|
+
return { model: requested, source: 'requested' };
|
|
141
|
+
let model = (target.model ?? '').trim();
|
|
142
|
+
if (model.length === 0) {
|
|
143
|
+
model = await firstModelId(ctx, target.provider);
|
|
144
|
+
// §7.180: 回填进缓存的目标对象 ⇒ 目录(listModels)、会话补钉与 wire
|
|
145
|
+
// 三处共享同一个 id(缓存里放的就是这个对象本身)。
|
|
146
|
+
if (model.length > 0)
|
|
147
|
+
target.model = model;
|
|
148
|
+
}
|
|
149
|
+
if (model.length === 0) {
|
|
150
|
+
// 落点解析到了、model id 拿不到 ⇒ 如实记下落点,模型名退回请求值。
|
|
151
|
+
return { model: requested, provider: target.provider, source: 'host-delegate', via: target.source };
|
|
152
|
+
}
|
|
153
|
+
return { model, provider: target.provider, source: 'host-delegate', via: target.source };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 解析「这个内部阶段这一轮真正会跑哪个模型」(池形态取自 config/edge.json)。
|
|
157
|
+
*
|
|
158
|
+
* @param ctx 宿主 ctx(读 `llm` / `agentDefaultModel` 两个软依赖)
|
|
159
|
+
* @param requested 调用方请求的模型 id(拿不到真相时的退路)
|
|
160
|
+
* @param ownProvider 调用方自己那条路由(`local-llm`),用于递归护栏
|
|
161
|
+
*/
|
|
162
|
+
export async function resolveEffectiveLocalModel(ctx, requested, ownProvider = 'local-llm') {
|
|
163
|
+
let pool;
|
|
164
|
+
try {
|
|
165
|
+
pool = localPool();
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
pool = {};
|
|
169
|
+
}
|
|
170
|
+
return resolveEffectiveLocalModelWith(ctx, pool, requested, ownProvider);
|
|
171
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* etscli-shell-guard — §7.120:封堵「bash 里直接调 etscli 写子命令」这条逃逸口
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么需要这个模块
|
|
5
|
+
*
|
|
6
|
+
* §7.98 的安全边界是**注册表白名单**:只把 14 个**只读** etscli 子命令注册进
|
|
7
|
+
* `ctx.tools`,写类(`file upload` / `file delete` / `fs rm` / `chat send` /
|
|
8
|
+
* `auth logout` / `migrate *` / `sharing create` / `meeting summary` /
|
|
9
|
+
* `file download` …)一个都不注册 ⇒ 模型在**工具清单**里选不到它们。
|
|
10
|
+
*
|
|
11
|
+
* 但 §7.118 那次实测把这条边界捅穿了:agent 直接
|
|
12
|
+
* `bash: etscli file upload ...`,把本地文件传到了 MEOS 设备的
|
|
13
|
+
* `/备忘/联系方式.txt`。原因(§7.120 查清):
|
|
14
|
+
*
|
|
15
|
+
* 1. 宿主的 **Auto review 闸门只在 `auto` preset 下安装**,本仓 edge preset 用
|
|
16
|
+
* `workspace-write` ⇒ 没有风险分类器;
|
|
17
|
+
* 2. 剩下的**沙箱越界升级是纯文件系统视角** —— `etscli file upload` 改的是
|
|
18
|
+
* **远端 MEOS 设备**(走 etscli 自己的协议),对文件系统沙箱完全不可见 ⇒
|
|
19
|
+
* 不越界 ⇒ 不升级 ⇒ **不触发审批**;
|
|
20
|
+
* 3. 于是 `bash` 这个通用逃逸口成了唯一的真身在侧:白名单管的是「工具清单」,
|
|
21
|
+
* 管不住「模型自己在 shell 里敲什么」。
|
|
22
|
+
*
|
|
23
|
+
* ## 主人裁决(2026-10-31)与机制落点
|
|
24
|
+
*
|
|
25
|
+
* > 用**插件侧守卫**封堵 —— 拦 bash 里 etscli 的写子命令。
|
|
26
|
+
*
|
|
27
|
+
* 裁决的实质是「**插件侧**封堵,不碰宿主启动环境(不改 PATH / 不改启动脚本),
|
|
28
|
+
* 桌面与无头两个 profile 都生效」。机制落点从 `ctx.tools.guard()` 换成
|
|
29
|
+
* `tools/pre-execute` waterfall,理由是三条**可查证**的事实:
|
|
30
|
+
*
|
|
31
|
+
* 1. **`ctx.tools.guard()` 不接受调用方的 scope。**
|
|
32
|
+
* 宿主实现是 `guard(guard) { return this.layers.effect(this.ctx, …) }`
|
|
33
|
+
* (`@deepseek-ai/dsh-tools` `lib/index.js:3017-3022`)—— 注册进的是
|
|
34
|
+
* **ToolRuntime 服务自己那个 ctx** 所在的层。插件侧拿到 `ctx.tools` 时,
|
|
35
|
+
* 拿不到、也改不了那个 ctx ⇒ **无法把守卫绑到边缘 preset 的 scope 上**;
|
|
36
|
+
* 服务在宿主根上下文提供时,那一层就是**全局层** ⇒ 标准模式会话也会被拦,
|
|
37
|
+
* 而标准模式会话的审计链必须**零本项目事件**(§7.140 不变式)。
|
|
38
|
+
* 2. **宿主的插件沙箱 façade 刻意不暴露 `guard`。**
|
|
39
|
+
* `dsh-cordis-host-runner/lib/types/guard.js:581-588` 给插件的 `ctx.tools`
|
|
40
|
+
* 只留 `register` / `schemas` / `get`(注释里点名 `guard` 属于
|
|
41
|
+
* 「identity protection, pre-policy, **monotonic guards**, …」这一串
|
|
42
|
+
* 不许旁路的机制)。桌面 profile 现在给的是真服务(`ctx.tools.view()`
|
|
43
|
+
* 能用 ⇒ boot scan 数出 28 个工具),但这属于**沙箱边界之外的当前状态**,
|
|
44
|
+
* 不是稳定契约。
|
|
45
|
+
* 3. **`tools/pre-execute` 既是宿主点名的扩展点,又是 scope 感知的。**
|
|
46
|
+
* 宿主注释原话(`lib/index.js:3008`):「Register a monotonic guard **after
|
|
47
|
+
* the extensible `tools/pre-execute` waterfall**」。派发时
|
|
48
|
+
* `carrier = scopeTarget(this, exec.agent)`(`lib/index.js:3320-3321`)——
|
|
49
|
+
* 监听器按 scope 链派发,**边缘 preset 的监听器只收到边缘会话的事件**,
|
|
50
|
+
* 标准模式会话的 scope 链上没有本插件的 scope ⇒ 收不到。
|
|
51
|
+
*
|
|
52
|
+
* 而 §7.136 已经把这条路在本仓跑通了:`realm-skill-bridge` 的 Layer 3 realm
|
|
53
|
+
* 闸门就是同一个 waterfall(`realm-skill-bridge.ts:1182`),审计链里有 6 条
|
|
54
|
+
* `tool.realm.denied`(`level: security`)作实证。
|
|
55
|
+
*
|
|
56
|
+
* 拒绝语义与 `guard()` 等价且同样**单调**:waterfall 判 `deny` 时
|
|
57
|
+
* `decision.reason` 直接成为拒绝理由(`lib/index.js:3337-3343`),工具**不派发**,
|
|
58
|
+
* 后续监听器与守卫都放不回来。
|
|
59
|
+
*
|
|
60
|
+
* ## 这条守卫堵的是「逃逸口」,不是「能力」
|
|
61
|
+
*
|
|
62
|
+
* 技能(`ets-file` / `ets-chat` / `ets-auth` …)走的是**宿主 skill 工具**
|
|
63
|
+
* (`exec.name === 'skill'`,参数 `skillName`),**不经过 bash**,因此**不受影响**
|
|
64
|
+
* —— 主人装的那些技能照常可用。本守卫只拦「模型自己在 shell 里敲 etscli」这条路。
|
|
65
|
+
*
|
|
66
|
+
* 诚实记录它的边界:**命令文本匹配可以被绝对路径调用、复制二进制、别名或拼接
|
|
67
|
+
* 绕过** ⇒ 它不是能力层的等价物。真正封死「模型按名字够到那个二进制」仍要靠
|
|
68
|
+
* 摘 PATH(主人本轮裁决不做)。两条是**纵深防御**关系。
|
|
69
|
+
*
|
|
70
|
+
* ## 依赖方向
|
|
71
|
+
*
|
|
72
|
+
* 本模块 import `etscli-tools.ts` 的 `ETSCLI_READONLY_ARGV`(白名单的**单一
|
|
73
|
+
* 事实源** = 从只读工具注册表派生的那份)—— **单向**依赖:`etscli-tools.ts`
|
|
74
|
+
* 不 import 本模块(否则成环:环在这里恰好不炸,那是求值顺序的运气)。
|
|
75
|
+
*
|
|
76
|
+
* ## 为什么是**独立插件行**,而不是写在 etscli-tools 里
|
|
77
|
+
*
|
|
78
|
+
* §7.118 那次 MEOS 写入事故发生在**headless HTTP profile**里,而那个 profile
|
|
79
|
+
* (以及 desktop-safe preset)**本来就没挂** `etscli-tools` ⇒ 守卫写在它里面
|
|
80
|
+
* 就等于**哪个部署都没上膛**(「代码写了、测试过了、闸门没装」是本仓最怕的
|
|
81
|
+
* 那种状态)。独立成行后可以**按需挂载**:
|
|
82
|
+
*
|
|
83
|
+
* - `profiles/dsh-edge-agent/cordis.patch.yml`(headless HTTP profile ——
|
|
84
|
+
* 事故发生的那个);
|
|
85
|
+
* - `presets/edge.desktop-safe.patch.yml`(桌面 preset);
|
|
86
|
+
* - `presets/edge.patch.yml`(完整 edge preset)。
|
|
87
|
+
*
|
|
88
|
+
* 独立行只**加一道闸**,**不**把那 14 个只读 etscli 工具带进那些 profile ——
|
|
89
|
+
* 带进去是能力扩张,超出「封堵逃逸口」这件事本身。
|
|
90
|
+
*
|
|
91
|
+
* @module dsh-edge-agent/etscli-shell-guard
|
|
92
|
+
*/
|
|
93
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
94
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
95
|
+
/** 插件名(cordis patch 行的 id 与本名一致)。 */
|
|
96
|
+
export declare const name = "etscli-shell-guard";
|
|
97
|
+
/**
|
|
98
|
+
* 没有服务依赖(§7.169):只用 `ctx.on`(cordis 的框架动词)。
|
|
99
|
+
*
|
|
100
|
+
* **刻意不声明 `auditLog`**:声明成硬依赖后,本行要等 auditLog 服务就绪
|
|
101
|
+
* 才 apply —— 那条服务一旦缺席,**闸门整个不装上**(逃逸口静默敞开)。
|
|
102
|
+
* 闸门必须先于任何证据通道就位。审计服务改为**延迟注入**捕获
|
|
103
|
+
* (见 apply() 内注释):审计是证据通道,不是闸门的依赖。
|
|
104
|
+
*/
|
|
105
|
+
export declare const inject: readonly [];
|
|
106
|
+
/** 能执行任意命令的工具名(小写比较)。 */
|
|
107
|
+
export declare const SHELL_TOOL_NAMES: readonly string[];
|
|
108
|
+
/**
|
|
109
|
+
* 只读子命令白名单:每项是一段 argv 前缀(`['fs','ls']` 匹配
|
|
110
|
+
* `fs ls --path /tmp`)。
|
|
111
|
+
*
|
|
112
|
+
* **由调用方传入**(单一事实源 = `etscli-tools.ts` 从注册表派生的那份),
|
|
113
|
+
* 本模块**不 import** 它 —— 否则 `etscli-tools → etscli-shell-guard →
|
|
114
|
+
* etscli-tools` 会成环:模块求值顺序一变就是 TDZ 崩,而崩点还是「运行期
|
|
115
|
+
> 才炸」的那种。
|
|
116
|
+
*/
|
|
117
|
+
export type EtscliReadonlyAllowlist = readonly (readonly string[])[];
|
|
118
|
+
/** 守卫的选项(白名单必填 —— 少传就是「白名单为空 ⇒ 全拒」的 fail-closed)。 */
|
|
119
|
+
export interface EtscliGuardOptions {
|
|
120
|
+
/** 只读子命令白名单(argv 前缀)。 */
|
|
121
|
+
allowlist: EtscliReadonlyAllowlist;
|
|
122
|
+
/** etscli 二进制路径(用于识别绝对路径调用)。 */
|
|
123
|
+
bin?: string;
|
|
124
|
+
/** 拒绝时的回调(证据要落盘)。 */
|
|
125
|
+
onDeny?: (denial: EtscliGuardDenial) => void;
|
|
126
|
+
}
|
|
127
|
+
/** 守卫的判定结果。 */
|
|
128
|
+
export interface EtscliShellVerdict {
|
|
129
|
+
/** 是否拒绝这次执行。 */
|
|
130
|
+
denied: boolean;
|
|
131
|
+
/** 被拦下的子命令(`file upload` 这种),用于审计与给模型的提示。 */
|
|
132
|
+
subcommand?: string;
|
|
133
|
+
/** 拒绝理由(返回给宿主,会变成工具结果里的 `Error: <reason>`)。 */
|
|
134
|
+
reason?: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 极简 shell 分词。
|
|
138
|
+
*
|
|
139
|
+
* 够用就好:这里的目的是**找出命令文本里有没有 etscli 调用**,不是做一个
|
|
140
|
+
* 完整的 shell 解析器。处理引号、转义、以及 `;` `|` `&` `>` `<` 这些分隔符,
|
|
141
|
+
* 好让 `etscli` 后面只取到属于它自己的那几个 token。
|
|
142
|
+
*/
|
|
143
|
+
export declare function tokenizeShell(command: string): string[];
|
|
144
|
+
/**
|
|
145
|
+
* 这串 argv 是不是白名单里的只读子命令?
|
|
146
|
+
*
|
|
147
|
+
* 判据用**前缀匹配**:白名单项 `['fs','ls']` 匹配 `fs ls --path /tmp`。
|
|
148
|
+
* 精确到 token,不会把 `fs lsx` 误判成 `fs ls`。
|
|
149
|
+
*/
|
|
150
|
+
export declare function isReadOnlyEtscliArgv(argv: readonly string[], allowlist: EtscliReadonlyAllowlist): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* 判定一条 shell 命令里有没有「不该出现的 etscli 调用」。
|
|
153
|
+
*
|
|
154
|
+
* 规则:**只放行白名单里那 14 个只读子命令**,其余一律拒绝。
|
|
155
|
+
*
|
|
156
|
+
* 为什么用白名单而不是黑名单(把 `file upload` / `fs rm` 这些逐个列出来禁):
|
|
157
|
+
* 黑名单只能挡住**已经想到的**写子命令 —— etscli 加一个新写命令,黑名单就漏一个。
|
|
158
|
+
* 白名单的漏法是**多挡**(新加的只读命令也会被挡),而白名单那份清单就在
|
|
159
|
+
* `etscli-tools.ts` 里(注册什么就放行什么),两者天然同步。
|
|
160
|
+
*
|
|
161
|
+
* @param command - shell 命令文本
|
|
162
|
+
* @param options.allowlist - 只读子命令白名单(argv 前缀)
|
|
163
|
+
* @param options.bin - 配置里的 etscli 二进制路径(用于识别绝对路径调用)
|
|
164
|
+
*/
|
|
165
|
+
export declare function evaluateEtscliShellCommand(command: string, options: {
|
|
166
|
+
allowlist: EtscliReadonlyAllowlist;
|
|
167
|
+
bin?: string;
|
|
168
|
+
}): EtscliShellVerdict;
|
|
169
|
+
/**
|
|
170
|
+
* 宿主执行信封(`dsh-tools` 的 exec 形状)的结构化描述。
|
|
171
|
+
*
|
|
172
|
+
* 参数字段名在宿主版本间会变(`arguments` / `args` / `input`),所以三个都读。
|
|
173
|
+
*/
|
|
174
|
+
export interface ToolExecLike {
|
|
175
|
+
name?: unknown;
|
|
176
|
+
arguments?: unknown;
|
|
177
|
+
args?: unknown;
|
|
178
|
+
input?: unknown;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 判定一次工具执行里有没有「不该出现的 etscli 调用」。
|
|
182
|
+
*
|
|
183
|
+
* 只看 **shell 类工具**(`bash` / `pwsh`)—— 技能走的是 `skill` 工具(参数
|
|
184
|
+
* `skillName`),不受影响;`etscli_*` 白名单工具走绝对路径直接调二进制,
|
|
185
|
+
* 也不经过 shell 工具。
|
|
186
|
+
*
|
|
187
|
+
* 参数值**不按参数名取**:把 exec 的参数对象里所有字符串值都过一遍。这样
|
|
188
|
+
* 不用依赖宿主 bash 工具的参数名(`command` / `cmd` / …),也不会因为参数名
|
|
189
|
+
* 变了就漏检。
|
|
190
|
+
*/
|
|
191
|
+
export declare function evaluateEtscliToolCall(exec: unknown, options: {
|
|
192
|
+
allowlist: EtscliReadonlyAllowlist;
|
|
193
|
+
bin?: string;
|
|
194
|
+
}): EtscliShellVerdict;
|
|
195
|
+
/** 守卫被拒绝时的回调载荷。 */
|
|
196
|
+
export interface EtscliGuardDenial {
|
|
197
|
+
/** 触发拒绝的工具名。 */
|
|
198
|
+
tool: string;
|
|
199
|
+
/** 被拦下的子命令。 */
|
|
200
|
+
subcommand: string;
|
|
201
|
+
/** 拒绝理由。 */
|
|
202
|
+
reason: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 把守卫注册到宿主的 `tools/pre-execute` waterfall。
|
|
206
|
+
*
|
|
207
|
+
* 契约(`@deepseek-ai/dsh-tools` `lib/index.js:3319-3343`):
|
|
208
|
+
*
|
|
209
|
+
* - 监听器返回 `{kind:'deny', reason}` ⇒ 这次执行**不派发**,`reason` 成为
|
|
210
|
+
* 拒绝理由(工具结果里的 `Error: <reason>`),后续监听器/守卫放不回来;
|
|
211
|
+
* - 放行时必须 `return next()` —— 直接 `allow` 会把后面的链条截断。
|
|
212
|
+
*
|
|
213
|
+
* 本监听器从**本插件自己的 ctx** 注册,而本插件挂在边缘 preset 的 scope 里
|
|
214
|
+
* ⇒ 宿主按 scope 链派发(`scopeTarget(this, exec.agent)`)⇒ 标准模式会话
|
|
215
|
+
* 收不到本事件 ⇒ 守卫不会伸到标准模式(§7.140 的零事件不变式不受影响)。
|
|
216
|
+
*
|
|
217
|
+
* @returns 宿主返回的**精确 disposer**(用于有序拆卸 / HMR 清理)
|
|
218
|
+
*/
|
|
219
|
+
export declare function registerEtscliShellGuard(ctx: Context, options: EtscliGuardOptions): () => void;
|
|
220
|
+
export interface Config {
|
|
221
|
+
/**
|
|
222
|
+
* etscli 二进制绝对路径 —— 只用来**识别**绝对路径调用
|
|
223
|
+
* (`/Users/me/.local/bin/etscli file upload …`)。
|
|
224
|
+
*
|
|
225
|
+
* 缺省顺序:本项 → `config/edge.json` 的 `etscli.bin` → `~/.local/bin/etscli`
|
|
226
|
+
* (§7.102「one config file」:运行期配置只有一个读点)。
|
|
227
|
+
*/
|
|
228
|
+
etscliBin?: string;
|
|
229
|
+
}
|
|
230
|
+
export declare const Config: Schema<Config>;
|
|
231
|
+
/**
|
|
232
|
+
* 装上这道闸。
|
|
233
|
+
*
|
|
234
|
+
* 装的顺序与两件记录:
|
|
235
|
+
*
|
|
236
|
+
* 1. **启动行**(`armed`):只记拒绝行的话,"守卫没装上" 和 "守卫装上了但这次
|
|
237
|
+
* 没人触发" 在日志里长得一模一样 ⇒ 启动时把**白名单规模**与**机制落点**
|
|
238
|
+
* 写出来。不记放行行(每次 bash 调用都记会淹掉真正的拒绝行;realm 闸门那层
|
|
239
|
+
* 已经在逐次记 tool allow/deny)。
|
|
240
|
+
* 2. **拒绝行**:ops log `warn` + 审计行 `etscli.shell.denied`
|
|
241
|
+
* (`level: security`;载荷**不含命令原文** —— 里面可能有本地路径与文件名)。
|
|
242
|
+
*/
|
|
243
|
+
export declare function apply(ctx: Context, config: Config): Promise<() => void>;
|
|
244
|
+
//# sourceMappingURL=etscli-shell-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"etscli-shell-guard.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/etscli-shell-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAK7C,qCAAqC;AACrC,eAAO,MAAM,IAAI,uBAAuB,CAAA;AAExC;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,aAAc,CAAA;AAEjC,yBAAyB;AACzB,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAqB,CAAA;AAEnE;;;;;;;;GAQG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAA;AAEpE,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,SAAS,EAAE,uBAAuB,CAAA;IAClC,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,qBAAqB;IACrB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAC7C;AAKD,eAAe;AACf,MAAM,WAAW,kBAAkB;IACjC,gBAAgB;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAKD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAuDvD;AAQD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,SAAS,EAAE,uBAAuB,GACjC,OAAO,CAIT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,SAAS,EAAE,uBAAuB,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D,kBAAkB,CAiDpB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAoBD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE;IAAE,SAAS,EAAE,uBAAuB,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D,kBAAkB,CAiBpB;AAED,mBAAmB;AACnB,MAAM,WAAW,iBAAiB;IAChC,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAcD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,kBAAkB,GAC1B,MAAM,IAAI,CA2BZ;AAID,MAAM,WAAW,MAAM;IACrB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CAEhC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAoE7E"}
|