@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.
Files changed (238) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/LICENSE +21 -0
  3. package/README.md +372 -2
  4. package/config/edge.example.json +179 -0
  5. package/config/privacy-policy.json +202 -0
  6. package/cordis.desktop-safe.patch.yml +129 -0
  7. package/cordis.patch.yml +382 -0
  8. package/cordis.yml +374 -0
  9. package/lib/attestation.d.ts +2 -0
  10. package/lib/attestation.d.ts.map +1 -0
  11. package/lib/attestation.js +2 -0
  12. package/lib/audit-log.d.ts +2 -0
  13. package/lib/audit-log.d.ts.map +1 -0
  14. package/lib/audit-log.js +2 -0
  15. package/lib/client.js +87 -0
  16. package/lib/cloud-llm-adapter.d.ts +2 -0
  17. package/lib/cloud-llm-adapter.d.ts.map +1 -0
  18. package/lib/cloud-llm-adapter.js +2 -0
  19. package/lib/dsh-adapter/attestation.d.ts +42 -0
  20. package/lib/dsh-adapter/attestation.d.ts.map +1 -0
  21. package/lib/dsh-adapter/attestation.js +40 -0
  22. package/lib/dsh-adapter/audit-log.d.ts +139 -0
  23. package/lib/dsh-adapter/audit-log.d.ts.map +1 -0
  24. package/lib/dsh-adapter/audit-log.js +376 -0
  25. package/lib/dsh-adapter/cloud-egress.d.ts +181 -0
  26. package/lib/dsh-adapter/cloud-egress.d.ts.map +1 -0
  27. package/lib/dsh-adapter/cloud-egress.js +257 -0
  28. package/lib/dsh-adapter/cloud-llm-adapter.d.ts +416 -0
  29. package/lib/dsh-adapter/cloud-llm-adapter.d.ts.map +1 -0
  30. package/lib/dsh-adapter/cloud-llm-adapter.js +1472 -0
  31. package/lib/dsh-adapter/contract.d.ts +80 -0
  32. package/lib/dsh-adapter/contract.d.ts.map +1 -0
  33. package/lib/dsh-adapter/contract.js +194 -0
  34. package/lib/dsh-adapter/desktop-cookie-janitor.d.ts +214 -0
  35. package/lib/dsh-adapter/desktop-cookie-janitor.d.ts.map +1 -0
  36. package/lib/dsh-adapter/desktop-cookie-janitor.js +409 -0
  37. package/lib/dsh-adapter/dsh-edge-agent.d.ts +48 -0
  38. package/lib/dsh-adapter/dsh-edge-agent.d.ts.map +1 -0
  39. package/lib/dsh-adapter/dsh-edge-agent.js +64 -0
  40. package/lib/dsh-adapter/edge-config.d.ts +484 -0
  41. package/lib/dsh-adapter/edge-config.d.ts.map +1 -0
  42. package/lib/dsh-adapter/edge-config.js +898 -0
  43. package/lib/dsh-adapter/edge-context-view.d.ts +184 -0
  44. package/lib/dsh-adapter/edge-context-view.d.ts.map +1 -0
  45. package/lib/dsh-adapter/edge-context-view.js +483 -0
  46. package/lib/dsh-adapter/effective-model.d.ts +85 -0
  47. package/lib/dsh-adapter/effective-model.d.ts.map +1 -0
  48. package/lib/dsh-adapter/effective-model.js +171 -0
  49. package/lib/dsh-adapter/etscli-shell-guard.d.ts +244 -0
  50. package/lib/dsh-adapter/etscli-shell-guard.d.ts.map +1 -0
  51. package/lib/dsh-adapter/etscli-shell-guard.js +420 -0
  52. package/lib/dsh-adapter/etscli-tools.d.ts +112 -0
  53. package/lib/dsh-adapter/etscli-tools.d.ts.map +1 -0
  54. package/lib/dsh-adapter/etscli-tools.js +393 -0
  55. package/lib/dsh-adapter/file-logger.d.ts +294 -0
  56. package/lib/dsh-adapter/file-logger.d.ts.map +1 -0
  57. package/lib/dsh-adapter/file-logger.js +564 -0
  58. package/lib/dsh-adapter/function-call-external.d.ts +357 -0
  59. package/lib/dsh-adapter/function-call-external.d.ts.map +1 -0
  60. package/lib/dsh-adapter/function-call-external.js +1005 -0
  61. package/lib/dsh-adapter/host-egress-guard.d.ts +161 -0
  62. package/lib/dsh-adapter/host-egress-guard.d.ts.map +1 -0
  63. package/lib/dsh-adapter/host-egress-guard.js +213 -0
  64. package/lib/dsh-adapter/host-llm-trace.d.ts +198 -0
  65. package/lib/dsh-adapter/host-llm-trace.d.ts.map +1 -0
  66. package/lib/dsh-adapter/host-llm-trace.js +344 -0
  67. package/lib/dsh-adapter/http-api-routes.d.ts +61 -0
  68. package/lib/dsh-adapter/http-api-routes.d.ts.map +1 -0
  69. package/lib/dsh-adapter/http-api-routes.js +79 -0
  70. package/lib/dsh-adapter/http-api-sessions.d.ts +105 -0
  71. package/lib/dsh-adapter/http-api-sessions.d.ts.map +1 -0
  72. package/lib/dsh-adapter/http-api-sessions.js +173 -0
  73. package/lib/dsh-adapter/http-api.d.ts +221 -0
  74. package/lib/dsh-adapter/http-api.d.ts.map +1 -0
  75. package/lib/dsh-adapter/http-api.js +1811 -0
  76. package/lib/dsh-adapter/index.d.ts +62 -0
  77. package/lib/dsh-adapter/index.d.ts.map +1 -0
  78. package/lib/dsh-adapter/index.js +97 -0
  79. package/lib/dsh-adapter/input-guard-messages.d.ts +56 -0
  80. package/lib/dsh-adapter/input-guard-messages.d.ts.map +1 -0
  81. package/lib/dsh-adapter/input-guard-messages.js +136 -0
  82. package/lib/dsh-adapter/input-guard.d.ts +508 -0
  83. package/lib/dsh-adapter/input-guard.d.ts.map +1 -0
  84. package/lib/dsh-adapter/input-guard.js +2174 -0
  85. package/lib/dsh-adapter/intent-prompts.d.ts +77 -0
  86. package/lib/dsh-adapter/intent-prompts.d.ts.map +1 -0
  87. package/lib/dsh-adapter/intent-prompts.js +97 -0
  88. package/lib/dsh-adapter/intent-router.d.ts +710 -0
  89. package/lib/dsh-adapter/intent-router.d.ts.map +1 -0
  90. package/lib/dsh-adapter/intent-router.js +2802 -0
  91. package/lib/dsh-adapter/intent-signals.d.ts +208 -0
  92. package/lib/dsh-adapter/intent-signals.d.ts.map +1 -0
  93. package/lib/dsh-adapter/intent-signals.js +391 -0
  94. package/lib/dsh-adapter/invariant-install.d.ts +16 -0
  95. package/lib/dsh-adapter/invariant-install.d.ts.map +1 -0
  96. package/lib/dsh-adapter/invariant-install.js +15 -0
  97. package/lib/dsh-adapter/invariant.d.ts +16 -0
  98. package/lib/dsh-adapter/invariant.d.ts.map +1 -0
  99. package/lib/dsh-adapter/invariant.js +22 -0
  100. package/lib/dsh-adapter/llm-adapters.d.ts +19 -0
  101. package/lib/dsh-adapter/llm-adapters.d.ts.map +1 -0
  102. package/lib/dsh-adapter/llm-adapters.js +16 -0
  103. package/lib/dsh-adapter/llm-internal-caller.d.ts +92 -0
  104. package/lib/dsh-adapter/llm-internal-caller.d.ts.map +1 -0
  105. package/lib/dsh-adapter/llm-internal-caller.js +100 -0
  106. package/lib/dsh-adapter/llm-max-turns.d.ts +55 -0
  107. package/lib/dsh-adapter/llm-max-turns.d.ts.map +1 -0
  108. package/lib/dsh-adapter/llm-max-turns.js +127 -0
  109. package/lib/dsh-adapter/llm-trace-store.d.ts +131 -0
  110. package/lib/dsh-adapter/llm-trace-store.d.ts.map +1 -0
  111. package/lib/dsh-adapter/llm-trace-store.js +205 -0
  112. package/lib/dsh-adapter/llm-trace.d.ts +450 -0
  113. package/lib/dsh-adapter/llm-trace.d.ts.map +1 -0
  114. package/lib/dsh-adapter/llm-trace.js +463 -0
  115. package/lib/dsh-adapter/local-llm-adapter.d.ts +141 -0
  116. package/lib/dsh-adapter/local-llm-adapter.d.ts.map +1 -0
  117. package/lib/dsh-adapter/local-llm-adapter.js +787 -0
  118. package/lib/dsh-adapter/local-storage.d.ts +96 -0
  119. package/lib/dsh-adapter/local-storage.d.ts.map +1 -0
  120. package/lib/dsh-adapter/local-storage.js +198 -0
  121. package/lib/dsh-adapter/model-router.d.ts +79 -0
  122. package/lib/dsh-adapter/model-router.d.ts.map +1 -0
  123. package/lib/dsh-adapter/model-router.js +107 -0
  124. package/lib/dsh-adapter/ohttp-gateway.d.ts +49 -0
  125. package/lib/dsh-adapter/ohttp-gateway.d.ts.map +1 -0
  126. package/lib/dsh-adapter/ohttp-gateway.js +32 -0
  127. package/lib/dsh-adapter/openai-sse.d.ts +85 -0
  128. package/lib/dsh-adapter/openai-sse.d.ts.map +1 -0
  129. package/lib/dsh-adapter/openai-sse.js +462 -0
  130. package/lib/dsh-adapter/pii-utils.d.ts +96 -0
  131. package/lib/dsh-adapter/pii-utils.d.ts.map +1 -0
  132. package/lib/dsh-adapter/pii-utils.js +324 -0
  133. package/lib/dsh-adapter/plugin-host.d.ts +38 -0
  134. package/lib/dsh-adapter/plugin-host.d.ts.map +1 -0
  135. package/lib/dsh-adapter/plugin-host.js +38 -0
  136. package/lib/dsh-adapter/plugin.d.ts +46 -0
  137. package/lib/dsh-adapter/plugin.d.ts.map +1 -0
  138. package/lib/dsh-adapter/plugin.js +100 -0
  139. package/lib/dsh-adapter/privacy-policy.d.ts +237 -0
  140. package/lib/dsh-adapter/privacy-policy.d.ts.map +1 -0
  141. package/lib/dsh-adapter/privacy-policy.js +391 -0
  142. package/lib/dsh-adapter/prompt-guard.d.ts +152 -0
  143. package/lib/dsh-adapter/prompt-guard.d.ts.map +1 -0
  144. package/lib/dsh-adapter/prompt-guard.js +872 -0
  145. package/lib/dsh-adapter/realm-skill-bridge.d.ts +365 -0
  146. package/lib/dsh-adapter/realm-skill-bridge.d.ts.map +1 -0
  147. package/lib/dsh-adapter/realm-skill-bridge.js +1180 -0
  148. package/lib/dsh-adapter/sanitizer-pipeline.d.ts +140 -0
  149. package/lib/dsh-adapter/sanitizer-pipeline.d.ts.map +1 -0
  150. package/lib/dsh-adapter/sanitizer-pipeline.js +431 -0
  151. package/lib/dsh-adapter/secure-bridge.d.ts +47 -0
  152. package/lib/dsh-adapter/secure-bridge.d.ts.map +1 -0
  153. package/lib/dsh-adapter/secure-bridge.js +35 -0
  154. package/lib/dsh-adapter/session-attribution.d.ts +70 -0
  155. package/lib/dsh-adapter/session-attribution.d.ts.map +1 -0
  156. package/lib/dsh-adapter/session-attribution.js +76 -0
  157. package/lib/dsh-adapter/sessions.d.ts +86 -0
  158. package/lib/dsh-adapter/sessions.d.ts.map +1 -0
  159. package/lib/dsh-adapter/sessions.js +179 -0
  160. package/lib/dsh-adapter/tcm.d.ts +55 -0
  161. package/lib/dsh-adapter/tcm.d.ts.map +1 -0
  162. package/lib/dsh-adapter/tcm.js +94 -0
  163. package/lib/dsh-adapter/td-bus.d.ts +87 -0
  164. package/lib/dsh-adapter/td-bus.d.ts.map +1 -0
  165. package/lib/dsh-adapter/td-bus.js +196 -0
  166. package/lib/dsh-adapter/tool-runner.d.ts +185 -0
  167. package/lib/dsh-adapter/tool-runner.d.ts.map +1 -0
  168. package/lib/dsh-adapter/tool-runner.js +271 -0
  169. package/lib/dsh-adapter/tools.d.ts +118 -0
  170. package/lib/dsh-adapter/tools.d.ts.map +1 -0
  171. package/lib/dsh-adapter/tools.js +292 -0
  172. package/lib/dsh-adapter/types.d.ts +23 -0
  173. package/lib/dsh-adapter/types.d.ts.map +1 -0
  174. package/lib/dsh-adapter/types.js +16 -0
  175. package/lib/dsh-adapter/voice-input.d.ts +30 -0
  176. package/lib/dsh-adapter/voice-input.d.ts.map +1 -0
  177. package/lib/dsh-adapter/voice-input.js +34 -0
  178. package/lib/dsh-adapter/web-search-tavily-bocha.d.ts +152 -0
  179. package/lib/dsh-adapter/web-search-tavily-bocha.d.ts.map +1 -0
  180. package/lib/dsh-adapter/web-search-tavily-bocha.js +289 -0
  181. package/lib/http-api.d.ts +2 -0
  182. package/lib/http-api.d.ts.map +1 -0
  183. package/lib/http-api.js +2 -0
  184. package/lib/index.d.ts +48 -0
  185. package/lib/index.d.ts.map +1 -0
  186. package/lib/index.js +49 -0
  187. package/lib/intent-router.d.ts +2 -0
  188. package/lib/intent-router.d.ts.map +1 -0
  189. package/lib/intent-router.js +2 -0
  190. package/lib/llm-adapters.d.ts +2 -0
  191. package/lib/llm-adapters.d.ts.map +1 -0
  192. package/lib/llm-adapters.js +2 -0
  193. package/lib/local-llm-adapter.d.ts +2 -0
  194. package/lib/local-llm-adapter.d.ts.map +1 -0
  195. package/lib/local-llm-adapter.js +2 -0
  196. package/lib/local-storage.d.ts +2 -0
  197. package/lib/local-storage.d.ts.map +1 -0
  198. package/lib/local-storage.js +2 -0
  199. package/lib/model-router.d.ts +2 -0
  200. package/lib/model-router.d.ts.map +1 -0
  201. package/lib/model-router.js +2 -0
  202. package/lib/ohttp-gateway.d.ts +2 -0
  203. package/lib/ohttp-gateway.d.ts.map +1 -0
  204. package/lib/ohttp-gateway.js +2 -0
  205. package/lib/plugin-host.d.ts +2 -0
  206. package/lib/plugin-host.d.ts.map +1 -0
  207. package/lib/plugin-host.js +2 -0
  208. package/lib/privacy-policy.d.ts +2 -0
  209. package/lib/privacy-policy.d.ts.map +1 -0
  210. package/lib/privacy-policy.js +2 -0
  211. package/lib/prompt-guard.d.ts +2 -0
  212. package/lib/prompt-guard.d.ts.map +1 -0
  213. package/lib/prompt-guard.js +2 -0
  214. package/lib/sanitizer-pipeline.d.ts +2 -0
  215. package/lib/sanitizer-pipeline.d.ts.map +1 -0
  216. package/lib/sanitizer-pipeline.js +2 -0
  217. package/lib/secure-bridge.d.ts +2 -0
  218. package/lib/secure-bridge.d.ts.map +1 -0
  219. package/lib/secure-bridge.js +2 -0
  220. package/lib/sessions.d.ts +2 -0
  221. package/lib/sessions.d.ts.map +1 -0
  222. package/lib/sessions.js +2 -0
  223. package/lib/tcm.d.ts +2 -0
  224. package/lib/tcm.d.ts.map +1 -0
  225. package/lib/tcm.js +2 -0
  226. package/lib/td-bus.d.ts +2 -0
  227. package/lib/td-bus.d.ts.map +1 -0
  228. package/lib/td-bus.js +2 -0
  229. package/lib/tools.d.ts +2 -0
  230. package/lib/tools.d.ts.map +1 -0
  231. package/lib/tools.js +2 -0
  232. package/lib/voice-input.d.ts +2 -0
  233. package/lib/voice-input.d.ts.map +1 -0
  234. package/lib/voice-input.js +2 -0
  235. package/package.json +341 -3
  236. package/presets/agent-preset-edge.json +50 -0
  237. package/presets/edge.desktop-safe.patch.yml +503 -0
  238. package/presets/edge.patch.yml +557 -0
@@ -0,0 +1,365 @@
1
+ /**
2
+ * realm-skill-bridge — Realm-aware skill and tool isolation for dsh-edge-agent.
3
+ *
4
+ * Reads a user-editable JSON config (`skill-realms.json`) that maps skill/tool
5
+ * names (or glob patterns) to LOCAL | CLOUD realms. Tags every discovered skill
6
+ * and tool accordingly, then enforces the mapping at three enforcement points:
7
+ *
8
+ * Layer 1 (agent/pre-step): filter the <available_skills> catalog so each
9
+ * agent only sees skills in its own realm. Skills tagged `hidden` are
10
+ * invisible to every agent.
11
+ *
12
+ * Layer 1.5-replacement (system-prompt/assemble): filter the tool schema
13
+ * list BEFORE dsh-agent-loop wraps it in deepFreeze and passes to the
14
+ * LLM. Replaces the deleted `llm/stream` tools probe — `payload.tools`
15
+ * at llm/stream is already frozen (dsh-agent-loop/lib/index.js:729
16
+ * `markAgentLoopRequest(deepFreeze({ ... tools: header.tools, ... }))`)
17
+ * so splice/defineProperty mutations silently fail there. The
18
+ * system-prompt/assemble waterfall value IS authoritative (per the dsh
19
+ * type declaration: "the returned value is authoritative") and tools are
20
+ * a plain array at that point. After this layer the LLM never sees
21
+ * out-of-realm tools.
22
+ *
23
+ * Layer 3 (tools/pre-execute): deny tool execution when the tool's realm
24
+ * does not match the calling agent's realm (defense-in-depth — also
25
+ * catches tools added via runtime registrations that bypass assembly).
26
+ *
27
+ * The config file is hot-reloaded via stat-polling (same pattern as
28
+ * privacy-policy.ts) so changes take effect without a restart.
29
+ *
30
+ * @module dsh-edge-agent/realm-skill-bridge
31
+ */
32
+ import type { Context } from '@deepseek-ai/cordis';
33
+ import { Service } from '@deepseek-ai/cordis';
34
+ import Schema from '@deepseek-ai/schemastery';
35
+ export declare const name = "realm-skill-bridge";
36
+ export declare const inject: readonly ["auditLog", "skills", "edgeTools", "tools"];
37
+ /** Realm tag for skills and tools. 'hidden' means invisible to all agents. */
38
+ export type RealmTag = 'LOCAL' | 'CLOUD' | 'SANITIZED' | 'hidden';
39
+ /**
40
+ * §7.93 Phase 2 (2026-09-24,§7.93.1 通用化 2026-09-25): intent categories
41
+ * function-call-external may request tool schemas for. REJECT never reaches
42
+ * the external caller (4xx before dispatch) and 'hidden' agents get no tools
43
+ * at all, so both are excluded from the input type; CLOUD is included for
44
+ * completeness even though the external path skips CLOUD intents
45
+ * (decision #4) — callers gate, this method stays total.
46
+ */
47
+ export type ExternalToolCallIntent = 'LOCAL' | 'CLOUD' | 'SANITIZED';
48
+ /**
49
+ * §7.93 Phase 2 (§7.93.1 通用化): one tool schema in the external caller's
50
+ * flat wire format (cactus-needle 3.0.1 / `~/work/needle/API.md` v1.1 §4.5:
51
+ * `{name, description, parameters}` —
52
+ * NOT the OpenAI `{type:'function', function:{...}}` envelope). This matches
53
+ * dsh-llm's own `ToolSchema` projection (`schemas()` whitelists exactly
54
+ * name/description/parameters), so the conversion is a shape check, not a
55
+ * translation.
56
+ */
57
+ export interface ExternalToolSchema {
58
+ name: string;
59
+ description: string;
60
+ parameters: Record<string, unknown>;
61
+ }
62
+ export interface Config {
63
+ /** Path to the skill-realms.json config file. */
64
+ configPath: string;
65
+ /** Whether to hot-reload the config on file change (via stat polling). */
66
+ hotReload: boolean;
67
+ /** Polling interval in ms for config change detection. */
68
+ watchIntervalMs: number;
69
+ }
70
+ export declare const Config: Schema<Config>;
71
+ /**
72
+ * Parsed shape of skill-realms.json.
73
+ *
74
+ * `skills` maps skill name or glob pattern (e.g. "lark-*") to a realm.
75
+ * `tools` maps tool name to a realm (overrides hard-coded DSH_NATIVE_REALM_MAP).
76
+ * `defaultRealm` applies to anything not explicitly matched. "hidden" = invisible.
77
+ *
78
+ * §7.150: `tools` 的值**允许** 'hidden'。运行时一直支持(既有测试就写着
79
+ * `tools: { secret_admin: 'hidden' }`,`loadConfig` 也不校验取值),只是这里
80
+ * 的类型比实际用法窄 —— 于是任何想屏蔽一个工具的写法都编译不过。类型跟实际
81
+ * 对齐,而不是让调用方绕道。
82
+ */
83
+ export interface RealmConfig {
84
+ skills: Record<string, RealmTag>;
85
+ tools: Record<string, RealmTag>;
86
+ defaultRealm: RealmTag;
87
+ }
88
+ interface AuditLogLike {
89
+ log(entry: {
90
+ level: string;
91
+ kind: string;
92
+ source: string;
93
+ payload?: Record<string, unknown>;
94
+ }): void;
95
+ }
96
+ /** Module augmentation: expose the service on ctx. */
97
+ declare module '@deepseek-ai/cordis' {
98
+ interface Context {
99
+ realmSkillBridge: RealmSkillBridgeService;
100
+ /** From @deepseek-ai/dsh-skill — augmented here to avoid importing the package. */
101
+ skills: {
102
+ list(options?: unknown): Promise<ReadonlyArray<{
103
+ name: string;
104
+ description?: string;
105
+ }>>;
106
+ snapshot(options?: unknown): Promise<{
107
+ skills: ReadonlyArray<{
108
+ name: string;
109
+ description?: string;
110
+ }>;
111
+ complete: boolean;
112
+ }>;
113
+ };
114
+ }
115
+ }
116
+ /**
117
+ * Minimal glob matcher — supports `*` as "any sequence of characters".
118
+ * We do not pull in the `minimatch` dependency for this; the only patterns
119
+ * used in skill-realms.json are simple suffix globs like "lark-*".
120
+ */
121
+ export declare function globMatch(pattern: string, value: string): boolean;
122
+ /**
123
+ * 把工具返回的 error message 变成一行可读的摘要。
124
+ *
125
+ * §7.151(2026-10-29)修:原来只有 `typeof message === 'string'` 才记内容,
126
+ * 否则一律打 `'unknown'`。但宿主的工具失败**大多数不是字符串** ——
127
+ * `deepseek-harness/docs/subsystems/user-questions.md:212` 明确写着
128
+ * `UserQuestionError` 继承 `HarnessError`,`ctx.tools.execute()` 保留
129
+ * `{ name, code }`(`NO_PROVIDER` / `BAD_INTENT` / `EMPTY_QUESTIONS` /
130
+ * `ASK_ABORTED`)。这些是**对象**,于是每一次这类失败在 ops log 里都只留下
131
+ * `errorKind=unknown` —— 真实错误码被吞掉,排查时无从下手
132
+ * (本轮就是这样误判了 ask_user_question 的失败原因)。
133
+ *
134
+ * 现在:字符串直接截断;对象/数组 JSON 序列化后截断;取不到就退回
135
+ * `String(message)`。**信息不能因为形态不是字符串就丢。**
136
+ */
137
+ export declare function describeToolError(message: unknown): string;
138
+ export declare class RealmSkillBridgeService extends Service {
139
+ private readonly config;
140
+ private readonly auditLog;
141
+ /** Skill name → realm tag (including 'hidden'). */
142
+ private readonly skillRealms;
143
+ /** Tool name → set of realm tags. A tool can belong to multiple realms. */
144
+ private readonly toolRealms;
145
+ /** Session id → realm. Written by http-api after intent classification. */
146
+ private readonly sessionRealms;
147
+ /** sessionRealms 的 LRU 上限;活跃会话远小于此(http-api 默认 8)。 */
148
+ private static readonly SESSION_REALMS_CAP;
149
+ /** Parsed realm config. Refreshed on hot-reload. */
150
+ private realmConfig;
151
+ /** mtime of the config file at last load (for stat-polling). */
152
+ private lastMtime;
153
+ /** Hot-reload timer handle. */
154
+ private watchTimer;
155
+ constructor(ctx: Context, config: Config, auditLog: AuditLogLike | undefined);
156
+ /** Boot: ensure config file exists, load it, tag skills/tools. */
157
+ start(): Promise<void>;
158
+ /** Stop hot-reload timer. */
159
+ stop(): void;
160
+ /** Drop all per-session realm tags (plugin dispose path). */
161
+ clearSessionRealms(): void;
162
+ /** Set the realm for a session (called by http-api after intent classification). */
163
+ setSessionRealm(sessionId: string, realm: RealmTag): void;
164
+ /** Resolve the realm for an agent by looking up its session.
165
+ * §7.36 / §7.37 (2026-09-04): prefer `session.id` — input-guard registers
166
+ * via `payload.agent.session.id` (input-guard.ts:537) and that is the
167
+ * only key under which sessionRealms holds the realm. Reading
168
+ * `session.header.id` first was a wrong assumption that caused layer-1.5
169
+ * (llm/stream tools filter) to silently no-op: getAgentRealm returned
170
+ * undefined → filterToolsField skipped → tools.filtered never fired. */
171
+ getAgentRealm(agent: AgentLike | undefined): RealmTag | undefined;
172
+ /**
173
+ * Resolve realm directly from a sessionId string. Used by llm/stream hook
174
+ * where the agent's session.id path is unreliable and payload.sessionId
175
+ * is the canonical key.
176
+ */
177
+ getSessionRealm(sessionId: string | undefined): RealmTag | undefined;
178
+ /** Look up the realm tag for a skill name.
179
+ * Prefers the cached tag (populated by tagSkills on skills/change), but
180
+ * falls back to resolveSkillRealm so skills not yet discovered still get
181
+ * the correct realm from the config file. */
182
+ skillRealm(name: string): RealmTag;
183
+ /** Look up the realm tags for a tool name. Returns undefined if no realm
184
+ * constraint is configured (tool is unconstrained). */
185
+ toolRealm(name: string): Set<RealmTag> | undefined;
186
+ private reloadConfig;
187
+ private checkForUpdates;
188
+ /**
189
+ * Resolve the realm for a skill by name.
190
+ * Priority:
191
+ * 1. skill-realms.json exact name match
192
+ * 2. skill-realms.json glob match (in declaration order)
193
+ * 3. defaultRealm
194
+ *
195
+ * Note: SKILL.md frontmatter `metadata.realm` is intentionally NOT consumed
196
+ * here — the config file is the single source of truth. This avoids a
197
+ * dependency on `ctx.skills.get()` (which reads the full markdown body)
198
+ * during the hot-path tag loop.
199
+ */
200
+ private resolveSkillRealm;
201
+ /** Re-scan ctx.skills.list() and tag each skill by realm. */
202
+ private tagSkills;
203
+ /** Synchronous variant for await contexts. */
204
+ private tagSkillsAsync;
205
+ private logTaggingResult;
206
+ /** Apply tool realm tags from skill-realms.json to ctx.edgeTools. */
207
+ private tagToolsFromConfig;
208
+ /**
209
+ * Filter the <available_skills> catalog so the agent only sees skills in
210
+ * its realm. Skills tagged 'hidden' are removed from every agent's catalog.
211
+ *
212
+ * §7.37 (2026-09-04): Two message shapes carry the catalog:
213
+ * (a) standalone UserMessage with source.kind === 'skill-catalog'
214
+ * (string content).
215
+ * (b) dsh-system-prompt plugin snapshot with
216
+ * source.kind === 'plugin' && source.plugin ===
217
+ * '@deepseek-ai/dsh-system-prompt' (array content blocks,
218
+ * one of which is type:'text' containing `<available_skills>`).
219
+ * Both must be filtered or the catalog leaks. (a) is the canonical
220
+ * skill-catalog shape; (b) is what TUI / WebUI actually see because
221
+ * dsh-system-prompt co-locates the catalog with sandbox/approval policy
222
+ * snapshots — without this filter, LLM sees ALL 32 skills regardless
223
+ * of realm.
224
+ *
225
+ * We parse each ` - \`skill-name\`: Description` line, look up the skill's
226
+ * realm, and drop disallowed ones.
227
+ */
228
+ filterSkillCatalog(messages: readonly MessageLike[], agentRealm: RealmTag): readonly MessageLike[];
229
+ /**
230
+ * §7.46 (2026-09-11): 返回 `{ messages, droppedCount }` 让 pre-step
231
+ * listener 能 emit `catalogDropped=N` 到 ops log(§7.44.1 chat-finished
232
+ * line 加 realm/duration 时同步加的;详见 PROGRESS §7.46)。返回 messages
233
+ * 是 filterSkillCatalog 的同一引用,方便测试不破坏。
234
+ */
235
+ filterSkillCatalogDetailed(messages: readonly MessageLike[], agentRealm: RealmTag): {
236
+ messages: readonly MessageLike[];
237
+ droppedCount: number;
238
+ };
239
+ /**
240
+ * Pure function: parse `<available_skills>` content, drop lines whose
241
+ * skill name resolves to a realm other than `agentRealm` (or 'hidden').
242
+ * Returns `{ text, changed }` so the caller can short-circuit on no-op.
243
+ *
244
+ * §7.45 (2026-09-11): SANITIZED agents see every non-hidden skill —
245
+ * matches `filterToolsField`'s SANITIZED branch below. Before this
246
+ * fix, `skillRealm(name)` only returns `LOCAL`/`CLOUD`/`hidden` (never
247
+ * `'SANITIZED'`), so `realm !== agentRealm` was always true for SANITIZED
248
+ * agents and the entire catalog was dropped (audit observed
249
+ * `droppedCount=33` for a session whose agentRealm=SANITIZED — LLM saw
250
+ * zero skills and silently answered "no mibao tool available"). Skill
251
+ * realm tag is fail-closed (default 'hidden'), so SANITIZED must
252
+ * explicitly opt out of the strict-equality check.
253
+ *
254
+ * §7.47.1 (2026-09-12): Same dual fix for LOCAL/CLOUD. The catalog
255
+ * filter uses `skillRealm` (single realm), but tools are tagged with a
256
+ * realm SET (`toolRealms: Set<RealmTag>`). `bash` skill.realm = 'LOCAL'
257
+ * yet bash.toolRealms = {LOCAL, CLOUD}. Before this fix, a CLOUD agent
258
+ * (e.g. news query) saw every LOCAL skill dropped — `droppedCount=11`
259
+ * for news session, identical to the SANITIZED bug. Symmetric for
260
+ * LOCAL agent: CLOUD skill (e.g. web_search, web_fetch) was dropped.
261
+ * The fix extends SANITIZED's opt-in to ALL agent realms: any
262
+ * non-'hidden' skill is kept in the catalog. Layer 3 (tools/pre-execute)
263
+ * remains the authoritative gate, mirroring `filterToolsField`'s
264
+ * semantics where any non-hidden tool is offered to the LLM and only
265
+ * blocked at execution time.
266
+ */
267
+ private filterCatalogText;
268
+ /**
269
+ * Pure function: filter the `tools` array carried on llm/stream payload so
270
+ * that the LLM does not see tools whose realm set does not include
271
+ * `agentRealm`. Tools whose realm is undefined (not in our config) are
272
+ * preserved (fail-closed: we only restrict what we explicitly tagged).
273
+ * Returns `{ tools, changed, removed }` so callers can short-circuit.
274
+ *
275
+ * SANITIZED special case: an agent whose input was sanitized may safely
276
+ * use both LOCAL tools (file ops don't leak) and CLOUD tools (input was
277
+ * already redacted). So SANITIZED agents see every non-hidden tool.
278
+ *
279
+ * §7.93 Phase 2 (§7.93.1 通用化): `options.audit === false` suppresses the
280
+ * `realm-skill-bridge.tools.filtered` audit entry. Used by
281
+ * getToolSchemasForExternal() so the LLM-path filter audit isn't duplicated
282
+ * per external call — function-call-external emits its own `external.*`
283
+ * audit kinds. Default (omitted) keeps the existing audited behavior.
284
+ */
285
+ filterToolsField(tools: ReadonlyArray<unknown>, agentRealm: RealmTag, options?: {
286
+ audit?: boolean;
287
+ sessionId?: string;
288
+ }): {
289
+ tools: unknown[];
290
+ changed: boolean;
291
+ removed: string[];
292
+ };
293
+ /**
294
+ * Build the external caller's wire-format tool schemas for one intent's realm.
295
+ *
296
+ * Pipeline: registry projection → realm filter (filterToolsField semantics,
297
+ * audit suppressed — caller emits `external.*` audit) → shape normalization.
298
+ *
299
+ * Failure contract: registry unavailable (cordis proxy throw before the
300
+ * `tools` service mounts, §7.92.2 trap) or empty → returns []. Callers MUST
301
+ * treat [] as "no external caller" and fall through to the LLM
302
+ * self-extraction path — never POST an empty tools array to the external
303
+ * caller (it would echo/hallucinate).
304
+ */
305
+ getToolSchemasForExternal(intent: ExternalToolCallIntent): ExternalToolSchema[];
306
+ /**
307
+ * Defensive read of `ctx.tools.schemas()` (global view). Returns [] when
308
+ * the service is not mounted yet or throws — the cordis proxy raises
309
+ * "cannot get property tools without inject" on unbound fibers, and
310
+ * tools/change re-fires tagging once available (same pattern as
311
+ * tagToolsFromConfig, §7.92.2).
312
+ */
313
+ private readRegistrySchemas;
314
+ /**
315
+ * Check whether a tool call is allowed in the calling agent's realm.
316
+ * Returns a deny reason string, or undefined if the call is allowed.
317
+ * A tool can belong to multiple realms (e.g. bash → LOCAL+CLOUD); the call
318
+ * is allowed if the agent's realm is in the tool's realm set.
319
+ */
320
+ checkToolRealm(toolName: string, agentRealm: RealmTag | undefined): string | undefined;
321
+ /** Log a realm denial to the audit log (security level). */
322
+ private auditDeny;
323
+ }
324
+ /**
325
+ * Normalize registry projections onto the external caller's flat wire contract
326
+ * (`{name, description, parameters}`; current cactus-needle 3.0.1 impl follows
327
+ * `~/work/needle/API.md` v1.1 §4.5):
328
+ *
329
+ * - entries without a non-empty string `name` are dropped (caller keys
330
+ * function_calls by name — a nameless schema is unexecutable)
331
+ * - missing/non-string `description` → '' (caller tolerates it; the tool
332
+ * name + parameters carry the selection signal)
333
+ * - missing/non-object `parameters` → permissive `{type:'object',
334
+ * properties:{}}` root (an unconstrained-args tool, e.g. no-arg status
335
+ * probes; JSON Schema semantics: absent properties = any object)
336
+ *
337
+ * Defensive by design: the registry projection is already whitelisted
338
+ * upstream, but a runtime-registered tool with a hand-rolled shape must not
339
+ * crash the external path — worst case it is dropped and the LLM path covers it.
340
+ */
341
+ export declare function normalizeExternalToolSchemas(tools: ReadonlyArray<unknown>): ExternalToolSchema[];
342
+ interface AgentLike {
343
+ session?: {
344
+ header?: {
345
+ id?: string;
346
+ cwd?: string;
347
+ };
348
+ id?: string;
349
+ };
350
+ }
351
+ interface MessageLike {
352
+ content?: unknown;
353
+ source?: {
354
+ kind?: string;
355
+ form?: string;
356
+ plugin?: string;
357
+ entries?: ReadonlyArray<{
358
+ name?: string;
359
+ }>;
360
+ };
361
+ role?: string;
362
+ }
363
+ export declare function apply(ctx: Context, config: Config): Promise<() => Promise<void>>;
364
+ export {};
365
+ //# sourceMappingURL=realm-skill-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realm-skill-bridge.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/realm-skill-bridge.ts"],"names":[],"mappings":"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"}