@dsh-edge/dsh-edge-agent 0.0.0-stage → 0.1.2

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 +158 -0
  2. package/LICENSE +21 -0
  3. package/README.md +181 -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 +253 -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,70 @@
1
+ /**
2
+ * §7.147 会话归属:审计行「属于哪个会话」的唯一口径。
3
+ *
4
+ * ## 为什么要有这个模块
5
+ *
6
+ * Edge 面板按**行**判断「这一轮」(§7.145-B):会话自己的行 `turn >= 0`,
7
+ * 全局行 `turn === -1`(哨兵)。归属在宿主侧由 `edge-context-view.ts` 的
8
+ * `extractSessionId()` 决定 —— 载荷里**有** sessionId 就是会话行,没有就是
9
+ * 全局行;全局桶会被合并进**任何一个**会话的快照。
10
+ *
11
+ * 实测(§7.147,2026-10-29,本机审计链 4962 行逐 kind 统计):
12
+ *
13
+ * | kind | 条数 | 带 sessionId |
14
+ * |---|---|---|
15
+ * | `llm.local.start` | 640 | **0** |
16
+ * | `llm.trace.index` | 195 | **0** |
17
+ * | `intent.jev.adopt` / `intent.jev.response` | 62 | **0** |
18
+ *
19
+ * 于是这些行**永远**落全局桶 ⇒ 路由 / 意图两个面板**结构上**不可能说
20
+ * 「这一轮」⇒ 面板只能显示「尚未绑定会话 + 全局信号」。
21
+ *
22
+ * ## 哨兵不是会话 id
23
+ *
24
+ * ops log 用 `'<pre-session>'` 表示「这次调用发生在会话建立之前」(§7.52:
25
+ * `<none>` / `unknown` 让人以为是数据丢失,`<pre-session>` 说明是预期)。
26
+ * 那是个**日志显示用**的哨兵,**不是**一个真实的会话 id。
27
+ *
28
+ * 把它当 sessionId 写进审计载荷会造出一个不存在的桶 —— 那些行从此只在
29
+ * 「有人请求 sessionId = `<pre-session>`」时才可见,等于**从所有真实会话
30
+ * 的视图里消失**。那比不写更糟:**宁可全局可见,不要无处可见**。
31
+ *
32
+ * 所以本模块给出唯一口径:
33
+ *
34
+ * - **生产侧**:`realSessionId()` / `sessionIdField()` —— 哨兵与空串一律
35
+ * 判成「没有会话」,载荷里干脆不写这个键。
36
+ * - **消费侧**:`edge-context-view.ts` 的 `extractSessionId()` 同样把哨兵
37
+ * 判成全局 —— 防御旧行与其它可能漏进来的路径。
38
+ *
39
+ * @module dsh-edge-agent/session-attribution
40
+ */
41
+ /**
42
+ * ops log 用的「无会话」哨兵(§7.52)。
43
+ *
44
+ * 它是**日志显示**用的,不是会话标识。任何把它当作 id 使用的地方都是 bug。
45
+ */
46
+ export declare const PRE_SESSION_SENTINEL = "<pre-session>";
47
+ /**
48
+ * 取「真实会话 id」——没有就是 `undefined`(= 归属全局桶)。
49
+ *
50
+ * 判为「没有会话」的输入:非字符串 / 空串 / `'<pre-session>'` 哨兵。
51
+ *
52
+ * @param value - 任意来源的候选 id(options.sessionId / trace.sessionId / …)
53
+ * @returns 真实会话 id,或 `undefined`
54
+ */
55
+ export declare function realSessionId(value: unknown): string | undefined;
56
+ /**
57
+ * 审计载荷上的 `sessionId` 字段 —— 有真会话才带这个键。
58
+ *
59
+ * 用 `...(id ? { sessionId: id } : {})`(本仓既有写法,见 `intent-router.ts`
60
+ * 的 `...(sessionId ? { sessionId } : {})`)而不是 `sessionId: id`:后者在
61
+ * 无会话时会写一个 `undefined` 字段。JSON.stringify 会把它丢掉,结果一样,
62
+ * 但显式 spread 更清楚「**没有**这个键」而不是「有个空值」。
63
+ *
64
+ * @param value - 任意来源的候选 id
65
+ * @returns 可直接展开进审计载荷的对象
66
+ */
67
+ export declare function sessionIdField(value: unknown): {
68
+ sessionId?: string;
69
+ };
70
+ //# sourceMappingURL=session-attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-attribution.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/session-attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,kBAAkB,CAAA;AAEnD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIhE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAGrE"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * §7.147 会话归属:审计行「属于哪个会话」的唯一口径。
3
+ *
4
+ * ## 为什么要有这个模块
5
+ *
6
+ * Edge 面板按**行**判断「这一轮」(§7.145-B):会话自己的行 `turn >= 0`,
7
+ * 全局行 `turn === -1`(哨兵)。归属在宿主侧由 `edge-context-view.ts` 的
8
+ * `extractSessionId()` 决定 —— 载荷里**有** sessionId 就是会话行,没有就是
9
+ * 全局行;全局桶会被合并进**任何一个**会话的快照。
10
+ *
11
+ * 实测(§7.147,2026-10-29,本机审计链 4962 行逐 kind 统计):
12
+ *
13
+ * | kind | 条数 | 带 sessionId |
14
+ * |---|---|---|
15
+ * | `llm.local.start` | 640 | **0** |
16
+ * | `llm.trace.index` | 195 | **0** |
17
+ * | `intent.jev.adopt` / `intent.jev.response` | 62 | **0** |
18
+ *
19
+ * 于是这些行**永远**落全局桶 ⇒ 路由 / 意图两个面板**结构上**不可能说
20
+ * 「这一轮」⇒ 面板只能显示「尚未绑定会话 + 全局信号」。
21
+ *
22
+ * ## 哨兵不是会话 id
23
+ *
24
+ * ops log 用 `'<pre-session>'` 表示「这次调用发生在会话建立之前」(§7.52:
25
+ * `<none>` / `unknown` 让人以为是数据丢失,`<pre-session>` 说明是预期)。
26
+ * 那是个**日志显示用**的哨兵,**不是**一个真实的会话 id。
27
+ *
28
+ * 把它当 sessionId 写进审计载荷会造出一个不存在的桶 —— 那些行从此只在
29
+ * 「有人请求 sessionId = `<pre-session>`」时才可见,等于**从所有真实会话
30
+ * 的视图里消失**。那比不写更糟:**宁可全局可见,不要无处可见**。
31
+ *
32
+ * 所以本模块给出唯一口径:
33
+ *
34
+ * - **生产侧**:`realSessionId()` / `sessionIdField()` —— 哨兵与空串一律
35
+ * 判成「没有会话」,载荷里干脆不写这个键。
36
+ * - **消费侧**:`edge-context-view.ts` 的 `extractSessionId()` 同样把哨兵
37
+ * 判成全局 —— 防御旧行与其它可能漏进来的路径。
38
+ *
39
+ * @module dsh-edge-agent/session-attribution
40
+ */
41
+ /**
42
+ * ops log 用的「无会话」哨兵(§7.52)。
43
+ *
44
+ * 它是**日志显示**用的,不是会话标识。任何把它当作 id 使用的地方都是 bug。
45
+ */
46
+ export const PRE_SESSION_SENTINEL = '<pre-session>';
47
+ /**
48
+ * 取「真实会话 id」——没有就是 `undefined`(= 归属全局桶)。
49
+ *
50
+ * 判为「没有会话」的输入:非字符串 / 空串 / `'<pre-session>'` 哨兵。
51
+ *
52
+ * @param value - 任意来源的候选 id(options.sessionId / trace.sessionId / …)
53
+ * @returns 真实会话 id,或 `undefined`
54
+ */
55
+ export function realSessionId(value) {
56
+ if (typeof value !== 'string')
57
+ return undefined;
58
+ if (value.length === 0 || value === PRE_SESSION_SENTINEL)
59
+ return undefined;
60
+ return value;
61
+ }
62
+ /**
63
+ * 审计载荷上的 `sessionId` 字段 —— 有真会话才带这个键。
64
+ *
65
+ * 用 `...(id ? { sessionId: id } : {})`(本仓既有写法,见 `intent-router.ts`
66
+ * 的 `...(sessionId ? { sessionId } : {})`)而不是 `sessionId: id`:后者在
67
+ * 无会话时会写一个 `undefined` 字段。JSON.stringify 会把它丢掉,结果一样,
68
+ * 但显式 spread 更清楚「**没有**这个键」而不是「有个空值」。
69
+ *
70
+ * @param value - 任意来源的候选 id
71
+ * @returns 可直接展开进审计载荷的对象
72
+ */
73
+ export function sessionIdField(value) {
74
+ const id = realSessionId(value);
75
+ return id === undefined ? {} : { sessionId: id };
76
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * sessions — M3 PLAINTEXT session store. One session = one KV entry in the
3
+ * `local-storage` collection `sessions`, keyed by `sessionId`. Each value
4
+ * carries the full message list, a creation time, and a lastSeenAt stamp.
5
+ *
6
+ * M3 NOTE — A3 replaces this with an encrypted + indexed store; the
7
+ * service contract (create / appendMessage / getRecentMessages /
8
+ * listActive / cleanup) is the durable shape.
9
+ *
10
+ * @module dsh-edge-agent/sessions
11
+ */
12
+ import type { Context } from '@deepseek-ai/cordis';
13
+ import { Service } from '@deepseek-ai/cordis';
14
+ import Schema from '@deepseek-ai/schemastery';
15
+ export declare const name = "sessions";
16
+ export declare const inject: readonly ["localStorage"];
17
+ export interface Config {
18
+ /** Default TTL in seconds; sessions idle longer than this are eligible for cleanup. */
19
+ defaultTtlSeconds: number;
20
+ /** Hard cap on cached sessions; cleanup prunes LRU first. */
21
+ maxConcurrentSessions: number;
22
+ /** Filename inside the local-storage root for the sessions collection. */
23
+ collection: string;
24
+ }
25
+ export declare const Config: Schema<Config>;
26
+ export interface SessionMessage {
27
+ role: 'user' | 'assistant' | 'system' | 'tool';
28
+ content: unknown;
29
+ ts: number;
30
+ }
31
+ export interface SessionRecord {
32
+ id: string;
33
+ createdAt: number;
34
+ lastSeenAt: number;
35
+ ttlSeconds: number;
36
+ messages: SessionMessage[];
37
+ /** Optional realm tag (LOCAL / CLOUD / SANITIZED) the session started under. */
38
+ realm?: 'LOCAL' | 'CLOUD' | 'SANITIZED';
39
+ }
40
+ export interface SessionsService {
41
+ create(input: {
42
+ id?: string;
43
+ realm?: SessionRecord['realm'];
44
+ ttlSeconds?: number;
45
+ }): Promise<SessionRecord>;
46
+ appendMessage(id: string, message: SessionMessage): Promise<SessionRecord>;
47
+ getRecentMessages(id: string, limit?: number): Promise<SessionMessage[]>;
48
+ get(id: string): Promise<SessionRecord | null>;
49
+ listActive(): Promise<SessionRecord[]>;
50
+ /** Drop sessions idle longer than their TTL; returns the count removed. */
51
+ cleanup(): Promise<number>;
52
+ /** Touch lastSeenAt without appending; preserves the TTL window. */
53
+ touch(id: string): Promise<void>;
54
+ }
55
+ export declare class SessionsServiceImpl extends Service {
56
+ private readonly collection;
57
+ private readonly defaultTtlSeconds;
58
+ private readonly maxConcurrentSessions;
59
+ /** 串行化 read-modify-write 周期:appendMessage / touch / cleanup 的
60
+ * get→put 之间会让出事件循环,并发交错时后写覆盖前写(丢消息/丢
61
+ * touch)。链内吞错防中毒 —— 一次失败不能让队列永久 rejected
62
+ * (local-storage.writeQueue 踩过的坑),错误如实抛给当次调用方。 */
63
+ private mutationQueue;
64
+ /** TTL/LRU 清扫定时器(apply 启动;unref 不阻塞进程退出)。 */
65
+ private sweepTimer;
66
+ private enqueue;
67
+ constructor(ctx: Context, config: Config);
68
+ create(input: {
69
+ id?: string;
70
+ realm?: SessionRecord['realm'];
71
+ ttlSeconds?: number;
72
+ }): Promise<SessionRecord>;
73
+ appendMessage(id: string, message: SessionMessage): Promise<SessionRecord>;
74
+ getRecentMessages(id: string, limit?: number): Promise<SessionMessage[]>;
75
+ get(id: string): Promise<SessionRecord | null>;
76
+ listActive(): Promise<SessionRecord[]>;
77
+ touch(id: string): Promise<void>;
78
+ cleanup(): Promise<number>;
79
+ /** Start the periodic TTL/LRU sweep; idempotent (apply 唯一调用方). */
80
+ startSweep(intervalMs?: number): void;
81
+ /** Stop the TTL/LRU sweep timer (plugin dispose path). */
82
+ stop(): void;
83
+ private storage;
84
+ }
85
+ export declare function apply(ctx: Context, config: Config): Promise<() => Promise<void>>;
86
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * sessions — M3 PLAINTEXT session store. One session = one KV entry in the
3
+ * `local-storage` collection `sessions`, keyed by `sessionId`. Each value
4
+ * carries the full message list, a creation time, and a lastSeenAt stamp.
5
+ *
6
+ * M3 NOTE — A3 replaces this with an encrypted + indexed store; the
7
+ * service contract (create / appendMessage / getRecentMessages /
8
+ * listActive / cleanup) is the durable shape.
9
+ *
10
+ * @module dsh-edge-agent/sessions
11
+ */
12
+ import { Service } from '@deepseek-ai/cordis';
13
+ import Schema from '@deepseek-ai/schemastery';
14
+ export const name = 'sessions';
15
+ export const inject = ['localStorage'];
16
+ export const Config = Schema.object({
17
+ defaultTtlSeconds: Schema.natural().default(3600),
18
+ maxConcurrentSessions: Schema.natural().default(64),
19
+ collection: Schema.string().default('sessions'),
20
+ });
21
+ // NOTE: declaration merging disabled to avoid conflict with upstream dsh-session's SessionStore.
22
+ // The edge-agent's session service is accessed via ctx.get('sessions') instead of ctx.sessions.
23
+ // declare module '@deepseek-ai/cordis' {
24
+ // interface Context {
25
+ // sessions: SessionsServiceImpl
26
+ // }
27
+ // }
28
+ export class SessionsServiceImpl extends Service {
29
+ collection;
30
+ defaultTtlSeconds;
31
+ maxConcurrentSessions;
32
+ /** 串行化 read-modify-write 周期:appendMessage / touch / cleanup 的
33
+ * get→put 之间会让出事件循环,并发交错时后写覆盖前写(丢消息/丢
34
+ * touch)。链内吞错防中毒 —— 一次失败不能让队列永久 rejected
35
+ * (local-storage.writeQueue 踩过的坑),错误如实抛给当次调用方。 */
36
+ mutationQueue = Promise.resolve();
37
+ /** TTL/LRU 清扫定时器(apply 启动;unref 不阻塞进程退出)。 */
38
+ sweepTimer;
39
+ enqueue(op) {
40
+ const result = this.mutationQueue.then(op, op);
41
+ this.mutationQueue = result.catch(() => { });
42
+ return result;
43
+ }
44
+ constructor(ctx, config) {
45
+ super(ctx, 'sessions');
46
+ this.collection = config.collection;
47
+ this.defaultTtlSeconds = config.defaultTtlSeconds;
48
+ this.maxConcurrentSessions = config.maxConcurrentSessions;
49
+ }
50
+ async create(input) {
51
+ const storage = this.storage();
52
+ const now = Date.now();
53
+ const id = input.id ?? `sess_${now}_${Math.random().toString(36).slice(2, 10)}`;
54
+ const session = {
55
+ id,
56
+ createdAt: now,
57
+ lastSeenAt: now,
58
+ ttlSeconds: input.ttlSeconds ?? this.defaultTtlSeconds,
59
+ messages: [],
60
+ realm: input.realm,
61
+ };
62
+ await storage.put(this.collection, id, session);
63
+ return session;
64
+ }
65
+ async appendMessage(id, message) {
66
+ return this.enqueue(async () => {
67
+ const storage = this.storage();
68
+ const session = await this.get(id);
69
+ if (!session) {
70
+ throw new Error(`sessions: session "${id}" not found`);
71
+ }
72
+ session.messages.push(message);
73
+ session.lastSeenAt = Date.now();
74
+ await storage.put(this.collection, id, session);
75
+ return session;
76
+ });
77
+ }
78
+ async getRecentMessages(id, limit) {
79
+ const session = await this.get(id);
80
+ if (!session)
81
+ return [];
82
+ const slice = limit === undefined ? session.messages : session.messages.slice(-limit);
83
+ return [...slice];
84
+ }
85
+ async get(id) {
86
+ const storage = this.storage();
87
+ const session = await storage.get(this.collection, id);
88
+ return session;
89
+ }
90
+ async listActive() {
91
+ const storage = this.storage();
92
+ const entries = await storage.list(this.collection);
93
+ const now = Date.now();
94
+ const out = [];
95
+ for (const { value } of entries) {
96
+ const session = value;
97
+ const ttlMs = session.ttlSeconds * 1000;
98
+ if (now - session.lastSeenAt <= ttlMs)
99
+ out.push(session);
100
+ }
101
+ out.sort((a, b) => b.lastSeenAt - a.lastSeenAt);
102
+ return out;
103
+ }
104
+ async touch(id) {
105
+ return this.enqueue(async () => {
106
+ const storage = this.storage();
107
+ const session = await storage.get(this.collection, id);
108
+ if (!session)
109
+ return;
110
+ session.lastSeenAt = Date.now();
111
+ await storage.put(this.collection, id, session);
112
+ });
113
+ }
114
+ async cleanup() {
115
+ return this.enqueue(async () => {
116
+ const storage = this.storage();
117
+ const entries = await storage.list(this.collection);
118
+ const now = Date.now();
119
+ let removed = 0;
120
+ for (const { key, value } of entries) {
121
+ const session = value;
122
+ const ttlMs = session.ttlSeconds * 1000;
123
+ if (now - session.lastSeenAt > ttlMs) {
124
+ await storage.delete(this.collection, key);
125
+ removed++;
126
+ }
127
+ }
128
+ // Enforce the cap by evicting oldest-by-lastSeenAt.
129
+ const active = await storage.list(this.collection);
130
+ if (active.length > this.maxConcurrentSessions) {
131
+ const sorted = (active
132
+ .map((e) => e.value)
133
+ .sort((a, b) => a.lastSeenAt - b.lastSeenAt));
134
+ const overflow = sorted.length - this.maxConcurrentSessions;
135
+ for (let i = 0; i < overflow; i++) {
136
+ const victim = sorted[i];
137
+ if (victim) {
138
+ await storage.delete(this.collection, victim.id);
139
+ removed++;
140
+ }
141
+ }
142
+ }
143
+ return removed;
144
+ });
145
+ }
146
+ /** Start the periodic TTL/LRU sweep; idempotent (apply 唯一调用方). */
147
+ startSweep(intervalMs = 60_000) {
148
+ if (this.sweepTimer !== undefined)
149
+ return;
150
+ this.sweepTimer = setInterval(() => {
151
+ void this.cleanup().catch(() => { });
152
+ }, intervalMs);
153
+ if (typeof this.sweepTimer.unref === 'function')
154
+ this.sweepTimer.unref();
155
+ }
156
+ /** Stop the TTL/LRU sweep timer (plugin dispose path). */
157
+ stop() {
158
+ if (this.sweepTimer !== undefined) {
159
+ clearInterval(this.sweepTimer);
160
+ this.sweepTimer = undefined;
161
+ }
162
+ }
163
+ storage() {
164
+ return this.ctx.localStorage;
165
+ }
166
+ }
167
+ export async function apply(ctx, config) {
168
+ // SessionsServiceImpl extends `Service`, so its constructor calls
169
+ // `ctx.reflect.provide('sessions', this)` for us. Do not call
170
+ // `ctx.provide` again — that would throw "service already registered".
171
+ const service = new SessionsServiceImpl(ctx, config);
172
+ // cleanup() 此前没有任何调用方 —— TTL/LRU 形同虚设。每 60s 清扫一次
173
+ // (unref,不阻塞进程退出),与 privacy-policy / realm-skill-bridge 的
174
+ // 定时器模式一致。
175
+ service.startSweep();
176
+ return async () => {
177
+ service.stop();
178
+ };
179
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * TCM (Trusted Computing Module) — provides TEE-aware key management,
3
+ * encryption, sealing, and remote-attestation primitives.
4
+ *
5
+ * Backends:
6
+ * - simulated: pure-JS, suitable for dev/CI
7
+ * - trustzone: ARM TrustZone (production target)
8
+ * - keystone: RISC-V Keystone (production target)
9
+ * - sgx: Intel SGX (server-side test)
10
+ *
11
+ * @module dsh-edge-agent/tcm
12
+ */
13
+ import { Service } from '@deepseek-ai/cordis';
14
+ import Schema from '@deepseek-ai/schemastery';
15
+ export declare const name = "tcm";
16
+ export declare const inject: readonly [];
17
+ export interface Config {
18
+ teeType: 'simulated' | 'trustzone' | 'keystone' | 'sgx';
19
+ keyStorePath: string;
20
+ teeInference: boolean;
21
+ secureMemoryLimit: number;
22
+ }
23
+ export declare const Config: Schema<Config>;
24
+ export interface AttestationToken {
25
+ payload: string;
26
+ signature: string;
27
+ teeType: string;
28
+ nonce: string;
29
+ }
30
+ export interface TcmStatus {
31
+ teeAvailable: boolean;
32
+ teeType: string;
33
+ keyCount: number;
34
+ secureMemoryUsed: number;
35
+ }
36
+ export declare class TcmService extends Service {
37
+ constructor(ctx: any);
38
+ generateKey(tag: string): Promise<Uint8Array>;
39
+ getKey(tag: string): Promise<Uint8Array>;
40
+ deleteKey(tag: string): Promise<void>;
41
+ encrypt(data: Uint8Array, tag: string): Promise<Uint8Array>;
42
+ decrypt(data: Uint8Array, tag: string): Promise<Uint8Array>;
43
+ hash(data: Uint8Array, algorithm: 'sha-256' | 'sha-3'): Promise<Uint8Array>;
44
+ seal(data: Uint8Array): Promise<Uint8Array>;
45
+ unseal(data: Uint8Array): Promise<Uint8Array>;
46
+ generateAttestation(nonce: string): Promise<AttestationToken>;
47
+ getStatus(): TcmStatus;
48
+ }
49
+ declare module '@deepseek-ai/cordis' {
50
+ interface Context {
51
+ tcm: TcmService;
52
+ }
53
+ }
54
+ export declare function apply(ctx: any, config: Config): Promise<() => Promise<void>>;
55
+ //# sourceMappingURL=tcm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tcm.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/tcm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAI7C,eAAO,MAAM,IAAI,QAAQ,CAAA;AAEzB,eAAO,MAAM,MAAM,aAAc,CAAA;AAEjC,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,KAAK,CAAA;IACvD,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,OAAO,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CAQhC,CAAA;AAEF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,qBAAa,UAAW,SAAQ,OAAO;gBACzB,GAAG,EAAE,GAAG;IAId,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAK7C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAIxC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAK3D,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI3D,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;IAI3E,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI3C,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7C,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IASnE,SAAS,IAAI,SAAS;CAQvB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,GAAG,EAAE,UAAU,CAAA;KAChB;CACF;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAmBlF"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * TCM (Trusted Computing Module) — provides TEE-aware key management,
3
+ * encryption, sealing, and remote-attestation primitives.
4
+ *
5
+ * Backends:
6
+ * - simulated: pure-JS, suitable for dev/CI
7
+ * - trustzone: ARM TrustZone (production target)
8
+ * - keystone: RISC-V Keystone (production target)
9
+ * - sgx: Intel SGX (server-side test)
10
+ *
11
+ * @module dsh-edge-agent/tcm
12
+ */
13
+ import { Service } from '@deepseek-ai/cordis';
14
+ import Schema from '@deepseek-ai/schemastery';
15
+ import { join } from 'node:path';
16
+ import { edgeHome, loadEdgeConfig } from './edge-config.js';
17
+ export const name = 'tcm';
18
+ export const inject = [];
19
+ export const Config = Schema.object({
20
+ // §7.102 "one config file": no required/default — apply() fills teeType /
21
+ // keyStorePath from config/edge.json (tee.*), deriving the keystore path
22
+ // from the storage root when not given.
23
+ teeType: Schema.union(['simulated', 'trustzone', 'keystone', 'sgx']),
24
+ keyStorePath: Schema.string(),
25
+ teeInference: Schema.boolean().default(false),
26
+ secureMemoryLimit: Schema.natural().default(64 * 1024 * 1024),
27
+ });
28
+ export class TcmService extends Service {
29
+ constructor(ctx) {
30
+ super(ctx, 'tcm');
31
+ }
32
+ async generateKey(tag) {
33
+ // MVP: stub. real implementation would call TEE SDK
34
+ return new Uint8Array(32);
35
+ }
36
+ async getKey(tag) {
37
+ return new Uint8Array(32);
38
+ }
39
+ async deleteKey(tag) {
40
+ // MVP: stub
41
+ }
42
+ async encrypt(data, tag) {
43
+ // MVP: stub. real implementation uses AES-256-GCM via TEE
44
+ return data;
45
+ }
46
+ async decrypt(data, tag) {
47
+ return data;
48
+ }
49
+ async hash(data, algorithm) {
50
+ return new Uint8Array(32);
51
+ }
52
+ async seal(data) {
53
+ return data;
54
+ }
55
+ async unseal(data) {
56
+ return data;
57
+ }
58
+ async generateAttestation(nonce) {
59
+ return {
60
+ payload: '',
61
+ signature: '',
62
+ teeType: 'simulated',
63
+ nonce,
64
+ };
65
+ }
66
+ getStatus() {
67
+ return {
68
+ teeAvailable: true,
69
+ teeType: 'simulated',
70
+ keyCount: 0,
71
+ secureMemoryUsed: 0,
72
+ };
73
+ }
74
+ }
75
+ export async function apply(ctx, config) {
76
+ // §7.102: fill unset fields from config/edge.json (tee.*) so the stub
77
+ // service and A3 future work read the same single source.
78
+ const tee = loadEdgeConfig().tee;
79
+ const c = config;
80
+ if (c.teeType === undefined) {
81
+ c.teeType = (tee?.type && tee.type.length > 0 ? tee.type : 'simulated');
82
+ }
83
+ if (c.keyStorePath === undefined) {
84
+ const storageRoot = tee?.storageRoot && tee.storageRoot.length > 0 ? tee.storageRoot : edgeHome();
85
+ c.keyStorePath = tee?.keyStorePath && tee.keyStorePath.length > 0
86
+ ? tee.keyStorePath
87
+ : join(storageRoot, 'tcm-keys.json');
88
+ }
89
+ ctx.plugin(TcmService);
90
+ // MVP: real TEE wiring (KeyStore, secure-memory limiter) lives in plugin.ts
91
+ return async () => {
92
+ // dispose hook
93
+ };
94
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * td-bus — threat detection bus.
3
+ *
4
+ * Phase D: real threat-level state machine. Subscribes to high-volume
5
+ * audit events and raises a threat level when patterns indicate abuse.
6
+ * The http-api plugin reads `getThreatLevel()` and blocks incoming
7
+ * `/v1/chat` requests when the level reaches the configured block
8
+ * threshold (default: 'critical', env DSH_EDGE_TD_BLOCK_LEVEL).
9
+ *
10
+ * Phase D decision matrix (frequency-driven):
11
+ * requests/min in window threat level
12
+ * ≥ thresholds.cloud... elevated
13
+ * ≥ 2 × thresholds... high
14
+ * ≥ 4 × thresholds... critical ← blockLevel default
15
+ *
16
+ * Other streams (failed-sanitization streak, permission-denied streak)
17
+ * are reserved for future escalation; Phase D only ships the cloud-
18
+ * requests-per-minute counter.
19
+ *
20
+ * @module dsh-edge-agent/td-bus
21
+ */
22
+ import { Service } from '@deepseek-ai/cordis';
23
+ import Schema from '@deepseek-ai/schemastery';
24
+ export declare const name = "td-bus";
25
+ export declare const inject: readonly ["auditLog", "privacyPolicy"];
26
+ export interface Config {
27
+ windowSeconds: number;
28
+ thresholds: {
29
+ /** §7.102: unset → filled from config/edge.json (threat.cloudRate),
30
+ * else the constructor literal 20. */
31
+ cloudRequestsPerMinute?: number;
32
+ failedSanitizationStreak: number;
33
+ permissionDeniedStreak: number;
34
+ };
35
+ /** Phase D: threat level at which the bus asks upstream plugins to
36
+ * block (e.g. http-api returns 503). §7.102: unset → edge.json
37
+ * (threat.blockLevel), else the constructor literal 'critical'. */
38
+ blockLevel?: 'elevated' | 'high' | 'critical';
39
+ }
40
+ export declare const Config: Schema<Config>;
41
+ export type ThreatLevel = 'normal' | 'elevated' | 'high' | 'critical';
42
+ export interface TdBusService {
43
+ getThreatLevel(): ThreatLevel;
44
+ /** Subscribe to a specific quarantine action. */
45
+ on(action: 'block-cloud' | 'kill-session' | 'notify-user' | 'lockdown', listener: () => void): () => void;
46
+ /** Manually escalate (e.g. after a policy tamper). */
47
+ escalate(reason: string): void;
48
+ /** Operator-facing解除 sticky 升级:escalate() 曾把等级永久钉死在
49
+ * critical(Phase D 无 reset 路径)。清空 sticky 与滑动窗口,回落
50
+ * normal;新流量按窗口重新累计。 */
51
+ reset(reason: string): void;
52
+ /** Phase D: ingest one cloud-bypass event (called by http-api / upstream). */
53
+ recordCloudRequest(): void;
54
+ }
55
+ export declare class TdBusServiceImpl extends Service {
56
+ private readonly windowMs;
57
+ private readonly cloudThreshold;
58
+ private readonly blockLevel;
59
+ private currentLevel;
60
+ /** When set, recomputeLevel() will not drop below this floor. Set by
61
+ * escalate(); cleared only when the sliding window fully decays. */
62
+ private stickyLevel;
63
+ /** Sliding-window timestamps of cloud requests. */
64
+ private readonly cloudHits;
65
+ private readonly listeners;
66
+ constructor(ctx: any, config?: Config);
67
+ /** Recompute threat level from the current window. Called after each
68
+ * recordCloudRequest() and from getThreatLevel(). Decay: cloudHits
69
+ * older than windowMs are shifted out first, so the count reflects
70
+ * only fresh events. */
71
+ private recomputeLevel;
72
+ getThreatLevel(): ThreatLevel;
73
+ on(action: 'block-cloud' | 'kill-session' | 'notify-user' | 'lockdown', listener: () => void): () => void;
74
+ escalate(reason: string): void;
75
+ recordCloudRequest(): void;
76
+ reset(reason: string): void;
77
+ /** Phase D: helper for http-api to decide whether to deny the request.
78
+ * Returns true iff current level is at or above the configured blockLevel. */
79
+ shouldBlock(): boolean;
80
+ }
81
+ declare module '@deepseek-ai/cordis' {
82
+ interface Context {
83
+ tdBus: TdBusServiceImpl;
84
+ }
85
+ }
86
+ export declare function apply(ctx: any, config: Config): Promise<() => Promise<void>>;
87
+ //# sourceMappingURL=td-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"td-bus.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/td-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAG7C,eAAO,MAAM,IAAI,WAAW,CAAA;AAE5B,eAAO,MAAM,MAAM,wCAAyC,CAAA;AAE5D,MAAM,WAAW,MAAM;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE;QACV;+CACuC;QACvC,sBAAsB,CAAC,EAAE,MAAM,CAAA;QAC/B,wBAAwB,EAAE,MAAM,CAAA;QAChC,sBAAsB,EAAE,MAAM,CAAA;KAC/B,CAAA;IACD;;wEAEoE;IACpE,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,CAAA;CAC9C;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CAYhC,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAA;AAErE,MAAM,WAAW,YAAY;IAC3B,cAAc,IAAI,WAAW,CAAA;IAC7B,iDAAiD;IACjD,EAAE,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IACzG,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B;;6BAEyB;IACzB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,8EAA8E;IAC9E,kBAAkB,IAAI,IAAI,CAAA;CAC3B;AAED,qBAAa,gBAAiB,SAAQ,OAAO;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAGvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,YAAY,CAAwB;IAC5C;yEACqE;IACrE,OAAO,CAAC,WAAW,CAA2B;IAC9C,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqG;gBAEnH,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM;IAOrC;;;6BAGyB;IACzB,OAAO,CAAC,cAAc;IAiCtB,cAAc,IAAI,WAAW;IAO7B,EAAE,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAOzG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAoB9B,kBAAkB,IAAI,IAAI;IAK1B,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgB3B;mFAC+E;IAC/E,WAAW,IAAI,OAAO;CAIvB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,KAAK,EAAE,gBAAgB,CAAA;KACxB;CACF;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CA6BlF"}