@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,344 @@
1
+ /**
2
+ * host-llm-trace — §7.144 记录**宿主 provider** 发出的 LLM 调用
3
+ *
4
+ * ## 为什么需要这个模块(主人现场发现)
5
+ *
6
+ * 主人在 Edge 面板看到「本轮走云端了,但 LLM 调用里没有云端模型」。查审计链
7
+ * 得到的硬事实是:那一轮**确实**判成了 CLOUD(jev 置信 0.83 < 门限 0.85 →
8
+ * 落到本地 LLM 分类器 → `llm-classified:cloud`),但整轮**零条**
9
+ * `llm.cloud.*` 事件 —— 只有 `llm.local.*`。
10
+ *
11
+ * 原因不是路由错了,是**观测漏了一跳**:
12
+ *
13
+ * - `config/edge.json` 的 `llm.cloud` 是 **delegate 模式**(`baseUrl` 空 +
14
+ * `delegate: true`,§7.100):CLOUD 请求**转发给宿主 profile 已配置的模型
15
+ * 提供商**(桌面 GUI 里选的那个,如 `qwen-token-plan-cn`),凭据全归宿主。
16
+ * - 于是主对话的请求**不经过本项目的 cloud adapter** ⇒ 没有 `llm.cloud.*`
17
+ * 审计事件,也没有 trace ⇒ 面板里云端模型整条缺席。
18
+ * - 面板是诚实的(它没编造),但**不完整** —— 主人看到的是「没发生过」,
19
+ * 实际是「没被记下来」。这正是 §7.129 最怕的那种误导:不是显示了一件
20
+ * 没发生的事,而是**漏显示**了一件真发生的事。
21
+ *
22
+ * ## 补在哪一跳
23
+ *
24
+ * 宿主的 `llm/stream` 事件带**完整信封**(`GenerateOptions`:provider /
25
+ * model / messages / system / tools / …),响应侧带**完整 chunk 流**
26
+ * (text-delta / block-end / tool-call-delta / usage / finish)。所以宿主
27
+ * provider 的这一跳**本来就完全可观测** —— 只是没人接。
28
+ *
29
+ * 本模块提供那一跳的记录能力;接线在 `input-guard` 的 `llm/stream` 钩子
30
+ * (那里已经握有 sessionId → 会话状态的映射,能区分「边缘会话」与「标准
31
+ * 模式会话」)。
32
+ *
33
+ * ## 与 adapter 记的那几条如何不重复
34
+ *
35
+ * 本项目自管两个池,provider 名固定:
36
+ *
37
+ * - `local-llm`(local adapter 注册名)
38
+ * - `cloud-llm`(cloud adapter 注册名;delegate 模式下它把请求**再**转发给
39
+ * 宿主 provider,那一跳的 provider 名是宿主自己的 ⇒ 正好被这里记到)
40
+ *
41
+ * 凡是 provider 属于自管池的调用,**adapter 自己已经记过**(REQ + RESP),
42
+ * 这里跳过 —— 否则面板上会出现同一通调用记两遍。
43
+ *
44
+ * ## 记什么
45
+ *
46
+ * | 侧 | 内容 |
47
+ * |---|---|
48
+ * | REQ | 宿主看到的信封:provider / model / messages / system / tools / 参数 |
49
+ * | RESP | 完整正文(block-end 优先)+ 工具调用 + finish reason + usage |
50
+ *
51
+ * `body` 快照是**宿主层信封**,不是 provider 的 wire body —— 后者在 adapter
52
+ * 内部,宿主这一跳看不见。面板上 provider/model 字段会显示宿主 provider
53
+ * 名(如 `qwen-token-plan-cn/deepseek-v4.1-flash`),一眼能认出「这是云端
54
+ * 模型那一条」。
55
+ *
56
+ * @module dsh-edge-agent/host-llm-trace
57
+ */
58
+ import { buildRequestTrace, buildResponseTrace, emitTrace, } from './llm-trace.js';
59
+ /* ── provider 判据 ───────────────────────────────────────────────────────── */
60
+ /**
61
+ * 本项目自管池的 provider 名(adapter 注册名)。
62
+ *
63
+ * 这两个名字来自 adapter 的注册表(cloud adapter 的 `providers` 默认
64
+ * `['cloud-llm']`),不是配置项 —— 改成别的名字会破坏 §7.100 的 delegate
65
+ * 语义,所以这里写死成常量。
66
+ */
67
+ export const OWN_POOL_PROVIDERS = ['local-llm', 'cloud-llm'];
68
+ /**
69
+ * §7.171 (2026-11-10): 「本地池 delegate 转发」标记。
70
+ *
71
+ * `local-llm` 走 delegate 时会把请求转发给宿主提供商(§7.171 新增),转发时
72
+ * `provider` 变成了**目标** provider(不再是 `local-llm`)⇒ `host-egress-guard`
73
+ * 会把它当成「宿主原生 provider 的出网那一跳」⇒ 按 §7.131 口径裁掉本地段历史。
74
+ *
75
+ * 但本地池的口径恰恰相反:**带全部历史**(§7.131 的不对称规则第一条)。
76
+ * ⇒ 转发时打这个标记,出网守卫看到它就放行(不裁)。
77
+ *
78
+ * 打在 options 上是安全的:宿主 `LlmRuntime.stream` 走
79
+ * `{...options, ...resolvedConfig}` 的 spread(`@deepseek-ai/dsh-llm`
80
+ * `lib/index.js` 的 `adapterStream`),未知字段原样透传,不会被拒。
81
+ *
82
+ * 住在这里(而不是 adapter 里)是因为**打标的一方与读标的一方分属两个模块**:
83
+ * `cloud-llm-adapter.ts`(打)↔ `host-egress-guard.ts`(读)。与
84
+ * `OWN_POOL_PROVIDERS` 同处一地 —— 自有池相关的判据只有这一份事实源。
85
+ */
86
+ export const LOCAL_POOL_FORWARD_MARKER = '__edgeLocalPoolForward';
87
+ /**
88
+ * 这个 provider 是不是本项目自己管的池?
89
+ *
90
+ * 是 ⇒ adapter 已经记过这次调用,这里必须跳过(否则同一通调用记两遍)。
91
+ * 否 ⇒ 宿主自己的 provider(delegate 出去的云池),**这里要补记**。
92
+ */
93
+ export function isOwnPoolProvider(provider, own = OWN_POOL_PROVIDERS) {
94
+ if (typeof provider !== 'string' || provider.length === 0)
95
+ return false;
96
+ return own.includes(provider);
97
+ }
98
+ /**
99
+ * 构造宿主调用的 request trace。
100
+ *
101
+ * `rawBody` 记的是**宿主层信封**(provider / model / messages / system /
102
+ * tools / 参数)—— 宿主这一跳能看到的全部。provider 的 wire body 在 adapter
103
+ * 内部,从这里看不见(那一侧由 adapter 自己记)。
104
+ */
105
+ export function buildHostRequestTrace(input) {
106
+ const r = input.request;
107
+ return buildRequestTrace({
108
+ sessionId: r.sessionId,
109
+ provider: r.provider,
110
+ model: r.model,
111
+ system: r.system,
112
+ messages: r.messages,
113
+ tools: r.tools,
114
+ params: r.params,
115
+ rawBody: {
116
+ provider: r.provider,
117
+ model: r.model,
118
+ messages: r.messages,
119
+ ...(r.system !== undefined ? { system: r.system } : {}),
120
+ ...(r.tools !== undefined ? { tools: r.tools } : {}),
121
+ ...(r.params !== undefined ? { params: r.params } : {}),
122
+ },
123
+ config: input.config,
124
+ });
125
+ }
126
+ /**
127
+ * 单次响应累积的字符上限。
128
+ *
129
+ * 观测路径不能变成故障源:异常大的响应(长文档粘贴 + 长工具回显)不能把
130
+ * 内存吃穿。超过上限后只保留前面这段,并在 trace 里如实标注(正文长度仍
131
+ * 按**真实**长度报,不假装完整)。
132
+ */
133
+ const MAX_ACCUM_CHARS = 200_000;
134
+ /**
135
+ * 从宿主的 chunk 流里累积一次响应。
136
+ *
137
+ * 正文优先取 `block-end` 的 `block.text`(宿主在 block 结束时给的是**全文**
138
+ * 比逐个 text-delta 拼更准);拿不到才回落累积 delta。
139
+ */
140
+ export class HostResponseAccumulator {
141
+ text = '';
142
+ textTruncated = false;
143
+ calls = new Map();
144
+ finishReason;
145
+ usage;
146
+ /** 吃掉一个 chunk。永不抛 —— 畸形 chunk 直接忽略。 */
147
+ push(chunk) {
148
+ if (chunk === null || typeof chunk !== 'object')
149
+ return;
150
+ const c = chunk;
151
+ switch (c.type) {
152
+ case 'text-delta': {
153
+ if (typeof c.text === 'string')
154
+ this.appendText(c.text);
155
+ return;
156
+ }
157
+ case 'block-end': {
158
+ const block = c.block;
159
+ if (block && block.type === 'text' && typeof block.text === 'string') {
160
+ // block-end 给的是**整块正文** —— 直接覆盖累积值(多 block 时以
161
+ // 最后一个文本 block 为准;agent-loop 的回答基本是单块)。
162
+ this.text = block.text.slice(0, MAX_ACCUM_CHARS);
163
+ this.textTruncated = block.text.length > MAX_ACCUM_CHARS;
164
+ }
165
+ return;
166
+ }
167
+ case 'tool-call-delta': {
168
+ const index = typeof c.index === 'number' ? c.index : this.calls.size;
169
+ const cur = this.calls.get(index) ?? { name: '', args: '' };
170
+ if (typeof c.id === 'string' && c.id.length > 0)
171
+ cur.id = c.id;
172
+ if (typeof c.name === 'string' && c.name.length > 0)
173
+ cur.name = c.name;
174
+ if (typeof c.argumentsDelta === 'string') {
175
+ cur.args = (cur.args + c.argumentsDelta).slice(0, MAX_ACCUM_CHARS);
176
+ }
177
+ this.calls.set(index, cur);
178
+ return;
179
+ }
180
+ case 'usage': {
181
+ const u = c.usage;
182
+ if (u && typeof u === 'object') {
183
+ const inputTokens = typeof u.inputTokens === 'number' ? u.inputTokens : undefined;
184
+ const outputTokens = typeof u.outputTokens === 'number' ? u.outputTokens : undefined;
185
+ this.usage = {
186
+ promptTokens: inputTokens,
187
+ completionTokens: outputTokens,
188
+ totalTokens: inputTokens !== undefined && outputTokens !== undefined
189
+ ? inputTokens + outputTokens
190
+ : undefined,
191
+ };
192
+ }
193
+ return;
194
+ }
195
+ case 'finish': {
196
+ // 宿主的 FinishReason 是**对象**({ kind: 'stop' } / { kind: 'tool-calls' } …),
197
+ // 直接 stringify 会变成 '{"kind":"stop"}' —— 面板上难看且不可比。优先取 kind。
198
+ const reason = c.reason;
199
+ if (typeof reason === 'string')
200
+ this.finishReason = reason;
201
+ else if (reason !== null && typeof reason === 'object') {
202
+ const kind = reason.kind;
203
+ this.finishReason = typeof kind === 'string' ? kind : safeStringify(reason);
204
+ }
205
+ else if (reason !== undefined)
206
+ this.finishReason = safeStringify(reason);
207
+ return;
208
+ }
209
+ default:
210
+ return;
211
+ }
212
+ }
213
+ appendText(text) {
214
+ if (this.text.length >= MAX_ACCUM_CHARS) {
215
+ this.textTruncated = true;
216
+ return;
217
+ }
218
+ const room = MAX_ACCUM_CHARS - this.text.length;
219
+ if (text.length > room) {
220
+ this.text += text.slice(0, room);
221
+ this.textTruncated = true;
222
+ return;
223
+ }
224
+ this.text += text;
225
+ }
226
+ /** 当前累积结果。 */
227
+ snapshot() {
228
+ const toolCalls = [...this.calls.entries()]
229
+ .sort((a, b) => a[0] - b[0])
230
+ .map(([, v]) => {
231
+ const call = { name: v.name };
232
+ if (v.id !== undefined)
233
+ call.id = v.id;
234
+ if (v.args.length > 0)
235
+ call.arguments = v.args;
236
+ return call;
237
+ });
238
+ const out = { content: this.text, toolCalls };
239
+ if (this.finishReason !== undefined)
240
+ out.finishReason = this.finishReason;
241
+ if (this.usage !== undefined)
242
+ out.usage = this.usage;
243
+ return out;
244
+ }
245
+ /** 累积时是否撞到上限(面板据此说明正文不全)。 */
246
+ get truncated() {
247
+ return this.textTruncated;
248
+ }
249
+ }
250
+ /** 构造宿主调用的 response trace。 */
251
+ export function buildHostResponseTrace(input) {
252
+ const s = input.snapshot;
253
+ return buildResponseTrace({
254
+ sessionId: input.sessionId,
255
+ provider: input.provider,
256
+ model: input.model,
257
+ durationMs: input.durationMs,
258
+ finishReason: s.finishReason,
259
+ content: s.content,
260
+ toolCalls: s.toolCalls,
261
+ usage: s.usage,
262
+ rawBody: {
263
+ content: s.content,
264
+ tool_calls: s.toolCalls,
265
+ finish_reason: s.finishReason,
266
+ usage: s.usage,
267
+ },
268
+ config: input.config,
269
+ });
270
+ }
271
+ /**
272
+ * 一次宿主 provider 调用的记录器。
273
+ *
274
+ * 构造即记 REQ;`wrap()` 包住 chunk 流,流结束时记 RESP。REQ/RESP 共用
275
+ * 同一个 callId ⇒ 面板上仍然「上请求、下响应」成对显示。
276
+ */
277
+ export class HostCallRecorder {
278
+ options;
279
+ accumulator = new HostResponseAccumulator();
280
+ startedAt = Date.now();
281
+ responseEmitted = false;
282
+ constructor(options) {
283
+ this.options = options;
284
+ try {
285
+ const { sink, config, request, callId } = options;
286
+ if (!config.enabled || !config.includeRequest)
287
+ return;
288
+ const trace = buildHostRequestTrace({ request, config });
289
+ emitTrace(sink, 'REQ', 'host-llm-trace LLM_REQUEST', trace, undefined, {
290
+ toolCount: request.tools?.length ?? 0,
291
+ callId,
292
+ });
293
+ }
294
+ catch { /* 观测失败不拖垮主链路 */ }
295
+ }
296
+ /** 包住 chunk 流:边转发边累积,流结束记 RESP。 */
297
+ async *wrap(stream) {
298
+ try {
299
+ for await (const chunk of stream) {
300
+ try {
301
+ this.accumulator.push(chunk);
302
+ }
303
+ catch { /* 单个 chunk 畸形不影响主链路 */ }
304
+ yield chunk;
305
+ }
306
+ }
307
+ finally {
308
+ this.emitResponse();
309
+ }
310
+ }
311
+ emitResponse() {
312
+ if (this.responseEmitted)
313
+ return;
314
+ this.responseEmitted = true;
315
+ try {
316
+ const { sink, config, request, callId } = this.options;
317
+ if (!config.enabled || !config.includeResponse)
318
+ return;
319
+ const trace = buildHostResponseTrace({
320
+ sessionId: request.sessionId,
321
+ provider: request.provider,
322
+ model: request.model,
323
+ snapshot: this.accumulator.snapshot(),
324
+ durationMs: Date.now() - this.startedAt,
325
+ config,
326
+ });
327
+ emitTrace(sink, 'RESP', 'host-llm-trace LLM_RESPONSE', trace, undefined, {
328
+ toolCount: request.tools?.length ?? 0,
329
+ callId,
330
+ });
331
+ }
332
+ catch { /* 观测失败不拖垮主链路 */ }
333
+ }
334
+ }
335
+ /* ── 小工具 ───────────────────────────────────────────────────────────────── */
336
+ /** 永不抛的 stringify(finish reason 可能是对象)。 */
337
+ function safeStringify(value) {
338
+ try {
339
+ return JSON.stringify(value) ?? String(value);
340
+ }
341
+ catch {
342
+ return '[unserializable]';
343
+ }
344
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * http-api-routes — pure route table for the dsh-edge-agent HTTP API.
3
+ *
4
+ * Why a separate module? Adding new endpoints used to mean appending a
5
+ * branch to `handleRequest()`'s if/else chain. With ~13 endpoints, that
6
+ * chain got long enough to make the dispatcher itself hard to read —
7
+ * and impossible to test in isolation. Extracting route definitions lets:
8
+ *
9
+ * 1. unit tests pin the exact set of routes + their auth/summary;
10
+ * 2. the dispatcher shrink to a generic "match → check auth → invoke";
11
+ * 3. OpenAPI spec generation iterate the same source of truth.
12
+ *
13
+ * Design choices:
14
+ * - Patterns use `{name}` placeholders. The matcher compiles each
15
+ * pattern into a RegExp once (at first match) and caches it.
16
+ * - Auth is `public` (no bearer) or `bearer` (require `Authorization`).
17
+ * We deliberately do NOT expose CORS / origin checks here — the
18
+ * dual-entry architecture document keeps CORS as a v2 concern
19
+ * (docs/dual-entry-architecture.md §7.1).
20
+ * - The handler signature is intentionally minimal: `(ctx, params)`.
21
+ * All HTTP plumbing (reqId, start, body, accessLog) is built into
22
+ * `ctx` so handlers don't have to know about the dispatcher.
23
+ *
24
+ * @module dsh-edge-agent/http-api-routes
25
+ */
26
+ import type { IncomingMessage, ServerResponse } from 'node:http';
27
+ export type RouteMethod = 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
28
+ export type Auth = 'public' | 'bearer';
29
+ /** Pre-parsed JSON body for routes that opt-in via `body`. `undefined`
30
+ * for routes that don't need a body (GET/DELETE). The dispatcher parses
31
+ * the body lazily — only when the matched route declares `body: 'json'`. */
32
+ export type RouteBody = any;
33
+ export interface RouteContext {
34
+ req: IncomingMessage;
35
+ res: ServerResponse;
36
+ reqId: string;
37
+ start: number;
38
+ body: RouteBody;
39
+ }
40
+ export interface RouteDef {
41
+ method: RouteMethod;
42
+ /** Path pattern with `{name}` placeholders, e.g. `/v1/sessions/{id}/cancel`. */
43
+ pattern: string;
44
+ auth: Auth;
45
+ /** Set to `'json'` for routes that accept a JSON body. The dispatcher
46
+ * parses the body BEFORE invoking the handler. */
47
+ body?: 'json';
48
+ /** Free-form summary used by OpenAPI spec generation. */
49
+ summary: string;
50
+ /** Async handler. Receives the per-request context and the matched
51
+ * path params (empty object when the pattern has no placeholders). */
52
+ handler: (ctx: RouteContext, params: Record<string, string>) => Promise<void> | void;
53
+ }
54
+ /** Match a `(method, path)` pair against the route table. Returns the
55
+ * matched route and the extracted path params, or `undefined` when
56
+ * nothing matches. */
57
+ export declare function findRoute(routes: readonly RouteDef[], method: string, path: string): {
58
+ route: RouteDef;
59
+ params: Record<string, string>;
60
+ } | undefined;
61
+ //# sourceMappingURL=http-api-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-api-routes.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/http-api-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAA;AAErE,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEtC;;6EAE6E;AAC7E,MAAM,MAAM,SAAS,GAAG,GAAG,CAAA;AAE3B,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,eAAe,CAAA;IACpB,GAAG,EAAE,cAAc,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,SAAS,CAAA;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,WAAW,CAAA;IACnB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,IAAI,CAAA;IACV;uDACmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAA;IACf;2EACuE;IACvE,OAAO,EAAE,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CACrF;AA2BD;;uBAEuB;AACvB,wBAAgB,SAAS,CACvB,MAAM,EAAE,SAAS,QAAQ,EAAE,EAC3B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,SAAS,CA4BjE"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * http-api-routes — pure route table for the dsh-edge-agent HTTP API.
3
+ *
4
+ * Why a separate module? Adding new endpoints used to mean appending a
5
+ * branch to `handleRequest()`'s if/else chain. With ~13 endpoints, that
6
+ * chain got long enough to make the dispatcher itself hard to read —
7
+ * and impossible to test in isolation. Extracting route definitions lets:
8
+ *
9
+ * 1. unit tests pin the exact set of routes + their auth/summary;
10
+ * 2. the dispatcher shrink to a generic "match → check auth → invoke";
11
+ * 3. OpenAPI spec generation iterate the same source of truth.
12
+ *
13
+ * Design choices:
14
+ * - Patterns use `{name}` placeholders. The matcher compiles each
15
+ * pattern into a RegExp once (at first match) and caches it.
16
+ * - Auth is `public` (no bearer) or `bearer` (require `Authorization`).
17
+ * We deliberately do NOT expose CORS / origin checks here — the
18
+ * dual-entry architecture document keeps CORS as a v2 concern
19
+ * (docs/dual-entry-architecture.md §7.1).
20
+ * - The handler signature is intentionally minimal: `(ctx, params)`.
21
+ * All HTTP plumbing (reqId, start, body, accessLog) is built into
22
+ * `ctx` so handlers don't have to know about the dispatcher.
23
+ *
24
+ * @module dsh-edge-agent/http-api-routes
25
+ */
26
+ const patternCache = new WeakMap();
27
+ function compilePattern(route) {
28
+ const cached = patternCache.get(route);
29
+ if (cached)
30
+ return cached;
31
+ const paramNames = [];
32
+ // Escape regex metachars except for `{...}` placeholders, which we
33
+ // turn into `([^/]+)` capture groups. The placeholder regex is
34
+ // deliberately restrictive — it won't accept path segments containing
35
+ // slashes, which matches URL semantics for our resource ids.
36
+ const escaped = route.pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\{([a-zA-Z_][a-zA-Z0-9_]*)\\\}/g, (_m, name) => {
37
+ paramNames.push(name);
38
+ return '([^/]+)';
39
+ });
40
+ const regex = new RegExp(`^${escaped}$`);
41
+ const compiled = { regex, paramNames };
42
+ patternCache.set(route, compiled);
43
+ return compiled;
44
+ }
45
+ /** Match a `(method, path)` pair against the route table. Returns the
46
+ * matched route and the extracted path params, or `undefined` when
47
+ * nothing matches. */
48
+ export function findRoute(routes, method, path) {
49
+ for (const route of routes) {
50
+ if (route.method !== method)
51
+ continue;
52
+ const { regex, paramNames } = compilePattern(route);
53
+ const m = regex.exec(path);
54
+ if (!m)
55
+ continue;
56
+ const params = {};
57
+ for (let i = 0; i < paramNames.length; i++) {
58
+ // `decodeURIComponent` throws URIError on malformed percent-escapes
59
+ // ('%zz'), and `new URL()` preserves such sequences verbatim in
60
+ // `pathname`. The matcher runs OUTSIDE any try/catch in the
61
+ // dispatcher (and the server callback drops the promise), so an
62
+ // unhandled URIError would take the whole process down — a remote
63
+ // DoS for anyone holding a token. Decode defensively: an undecodable
64
+ // param keeps its raw form and the request continues (the handler
65
+ // then answers 404/400 on an unknown id rather than crashing).
66
+ const raw = m[i + 1];
67
+ let decoded = raw;
68
+ try {
69
+ decoded = decodeURIComponent(raw);
70
+ }
71
+ catch {
72
+ decoded = raw;
73
+ }
74
+ params[paramNames[i]] = decoded;
75
+ }
76
+ return { route, params };
77
+ }
78
+ return undefined;
79
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * http-api-sessions — soft-dep wrapper around the `sessions` plugin
3
+ * (`@deepseek-ai/dsh-session-persistence-jsonl`). Provides a stable
4
+ * surface for the http-api endpoints (`/v1/sessions/{cancel,rename,
5
+ * fork,...}`) without hard-binding to the upstream plugin's exact API.
6
+ *
7
+ * Why a wrapper layer?
8
+ * - The upstream `sessions` plugin's API may grow with each DSH
9
+ * release. The wrapper freezes a v1-stable contract:
10
+ * createSession / getSession / listSessions /
11
+ * renameSession / forkSession / cancelSession / deleteSession
12
+ * - Soft-dep pattern: if the `sessions` plugin isn't registered
13
+ * (older test profiles, minimal config), the wrapper falls back
14
+ * to in-memory metadata. This keeps tests simple and decouples
15
+ * from boot-order races.
16
+ * - Title rename needs a `payload` map the upstream plugin doesn't
17
+ * expose directly. The wrapper stores titles as audit-log entries
18
+ * keyed by `payload.sessionId + payload.title` (cheap, recoverable
19
+ * across restarts, and already persisted by the audit-log plugin).
20
+ *
21
+ * @module dsh-edge-agent/http-api-sessions
22
+ */
23
+ export interface SessionMetadata {
24
+ sessionId: string;
25
+ /** Display title (rename-able). May be null for sessions that haven't
26
+ * been renamed. */
27
+ title: string | null;
28
+ createdAt: number;
29
+ lastAccessedAt: number;
30
+ /** Optional provider/model hint captured at session creation time.
31
+ * Stored as opaque strings — http-api doesn't enforce they match
32
+ * a registered provider. */
33
+ provider?: string;
34
+ model?: string;
35
+ }
36
+ export interface SessionCreateInput {
37
+ sessionId?: string;
38
+ provider?: string;
39
+ model?: string;
40
+ /** Optional realm tag (LOCAL / CLOUD / SANITIZED). Used by the realm-
41
+ * skill-bridge layer to filter skill catalogs. Stored here for
42
+ * diagnostic / restore purposes. */
43
+ realm?: string;
44
+ }
45
+ export interface SessionForkInput {
46
+ /** Source session id to fork from. */
47
+ sourceSessionId: string;
48
+ /** Optional new session id; defaults to a fresh UUID. */
49
+ newSessionId?: string;
50
+ }
51
+ export interface HttpApiSessions {
52
+ /** Create a new session record. Returns the assigned sessionId. */
53
+ createSession(input?: SessionCreateInput): Promise<{
54
+ sessionId: string;
55
+ }>;
56
+ /** Read a session's metadata. Returns null if not found. */
57
+ getSession(sessionId: string): Promise<SessionMetadata | null>;
58
+ /** List all sessions known to this wrapper. Newest-first by
59
+ * lastAccessedAt. */
60
+ listSessions(): Promise<SessionMetadata[]>;
61
+ /** Update the human-readable title. */
62
+ renameSession(sessionId: string, title: string): Promise<void>;
63
+ /** Fork: copy a session's history under a new sessionId. The source
64
+ * session is left untouched. Returns the new sessionId. */
65
+ forkSession(input: SessionForkInput): Promise<{
66
+ sessionId: string;
67
+ sourceSessionId: string;
68
+ }>;
69
+ /** Cancel: best-effort signal that the session's turn should abort.
70
+ * The actual abort is performed by the http-api service (which holds
71
+ * the AgentHandle). The wrapper just records intent. */
72
+ cancelSession(sessionId: string): Promise<void>;
73
+ /** Delete: remove the session record entirely. Returns true if a
74
+ * record was deleted, false if no such session existed. */
75
+ deleteSession(sessionId: string): Promise<boolean>;
76
+ }
77
+ /** Soft-dep constructor: pass the cordis context; the wrapper will
78
+ * duck-type `ctx.sessions` or `ctx.get?.('sessions')` lazily on each
79
+ * call. This keeps boot-order races from breaking http-api. */
80
+ export declare class HttpApiSessionsImpl implements HttpApiSessions {
81
+ private cordisCtx;
82
+ /** In-memory metadata fallback when the upstream plugin is absent. */
83
+ private readonly memory;
84
+ /** Recently-deleted session ids we want to remember briefly so a
85
+ * post-cancel /v1/sessions/{id} doesn't immediately return 200.
86
+ * Cleared on a timer so it doesn't grow unbounded. */
87
+ private readonly tombstone;
88
+ constructor(cordisCtx: any);
89
+ /** Lazy upstream plugin lookup. Returns null when the plugin isn't
90
+ * registered (older profiles / tests). */
91
+ private upstream;
92
+ createSession(input?: SessionCreateInput): Promise<{
93
+ sessionId: string;
94
+ }>;
95
+ getSession(sessionId: string): Promise<SessionMetadata | null>;
96
+ listSessions(): Promise<SessionMetadata[]>;
97
+ renameSession(sessionId: string, title: string): Promise<void>;
98
+ forkSession(input: SessionForkInput): Promise<{
99
+ sessionId: string;
100
+ sourceSessionId: string;
101
+ }>;
102
+ cancelSession(sessionId: string): Promise<void>;
103
+ deleteSession(sessionId: string): Promise<boolean>;
104
+ }
105
+ //# sourceMappingURL=http-api-sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-api-sessions.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/http-api-sessions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB;wBACoB;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB;;iCAE6B;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;yCAEqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,eAAe,EAAE,MAAM,CAAA;IACvB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,aAAa,CAAC,KAAK,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACzE,4DAA4D;IAC5D,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;IAC9D;0BACsB;IACtB,YAAY,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IAC1C,uCAAuC;IACvC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D;gEAC4D;IAC5D,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7F;;6DAEyD;IACzD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C;gEAC4D;IAC5D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACnD;AAeD;;gEAEgE;AAChE,qBAAa,mBAAoB,YAAW,eAAe;IAQ7C,OAAO,CAAC,SAAS;IAP7B,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAC5D;;2DAEuD;IACvD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;gBAE1B,SAAS,EAAE,GAAG;IAElC;+CAC2C;IAC3C,OAAO,CAAC,QAAQ;IAaV,aAAa,CAAC,KAAK,GAAE,kBAAuB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAoB7E,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAW9D,YAAY,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAa1C,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9D,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAe7F,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAWzD"}