@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,205 @@
1
+ /**
2
+ * llm-trace-store — §7.133 LLM request/response trace 的**正文存储**
3
+ *
4
+ * ## 为什么正文不进 audit 行
5
+ *
6
+ * §7.118 的 trace 走 `ctx.logger.info` → ops log 文件。而 Edge 面板的数据
7
+ * 源是 **audit-log**(edge-context-view 的 fold)—— 两条通路不相通,所以
8
+ * 面板看不到任何 request/response 正文。
9
+ *
10
+ * 修法有两条路:
11
+ *
12
+ * A. 正文直接进 audit 行 —— 面板立刻能看到,但单轮主对话的 request
13
+ * 正文实测 **22.5 万字符**(promptLength=225630)。几十轮之后,
14
+ * 面板的常驻内存和渲染压力线性膨胀。
15
+ * B. 面板只放**索引**(每次调用一行摘要),正文按需拉取 —— 索引行是
16
+ * 几十字节的标量,正文存在独立的 store 里,点开才取。
17
+ *
18
+ * 选 B。这与本仓一贯的纪律一致(§7.107「面板不得断言与审计数据相矛盾
19
+ * 的事」、§7.129「宁可漏显示也不能显示一件没发生过的事」):面板常驻
20
+ * 的永远是**便宜的索引**,正文是**要才取**。
21
+ *
22
+ * ## store 的形状
23
+ *
24
+ * `traceId` → { kind: 'REQ' | 'RESP', caller?, trace }。ring buffer:
25
+ * 条数与总字节双上限 —— 单条 trace 正文上限 2 万字符(trace 模块自己
26
+ * 已按 maxChars 截断),所以 64 条 ≈ 1.3 MB 上界。
27
+ *
28
+ * ## 谁来写
29
+ *
30
+ * adapter(主对话出网)+ prompt-guard(安检)+ intent-router(分类器)。
31
+ * 三处都经 `emitTrace()` —— 它同时落 **audit 索引行**(进面板)与
32
+ * **正文**(进本 store)。
33
+ *
34
+ * 检测调用的标记沿用 §7.132 的 `edgeInternalCaller`:安检 = prompt-guard,
35
+ * 分类器 = intent-classifier,主对话没有这个字段。面板据此把「检测调用」
36
+ * 与「主对话出网」分开显示。
37
+ *
38
+ * @module dsh-edge-agent/llm-trace-store
39
+ */
40
+ /** 默认值:64 条 / 2 MB。 */
41
+ export const DEFAULT_TRACE_STORE_OPTIONS = {
42
+ maxEntries: 64,
43
+ maxBytes: 2 * 1024 * 1024,
44
+ };
45
+ /** 一条 trace 的粗略字节数(够用即可,不追求精确)。 */
46
+ function approxBytes(value) {
47
+ try {
48
+ return JSON.stringify(value)?.length ?? 0;
49
+ }
50
+ catch {
51
+ return 0;
52
+ }
53
+ }
54
+ /**
55
+ * trace 正文存储。
56
+ *
57
+ * 纯内存 —— 进程退出即消失,这是对的:正文是**当轮**的观测数据,不是
58
+ * 审计记录(审计记录在 audit.plain.jsonl 里,且只存标量)。
59
+ */
60
+ export class LlmTraceStore {
61
+ entries = new Map();
62
+ order = [];
63
+ bytes = 0;
64
+ opts;
65
+ constructor(options = DEFAULT_TRACE_STORE_OPTIONS) {
66
+ this.opts = options;
67
+ }
68
+ /** 存一条 trace,返回它的 id(调用方拿去写索引行)。 */
69
+ put(id, entry) {
70
+ if (this.entries.has(id)) {
71
+ // 同 id 重写(理论上不会):先扣掉旧的字节数。
72
+ this.bytes -= approxBytes(this.entries.get(id)?.trace);
73
+ this.entries.set(id, entry);
74
+ this.bytes += approxBytes(entry.trace);
75
+ return;
76
+ }
77
+ this.entries.set(id, entry);
78
+ this.order.push(id);
79
+ this.bytes += approxBytes(entry.trace);
80
+ this.evict();
81
+ }
82
+ /** 取一条 trace;不存在返回 undefined。 */
83
+ get(id) {
84
+ return this.entries.get(id);
85
+ }
86
+ /** 当前条数(面板显示「已记录 N 次调用」用)。 */
87
+ size() {
88
+ return this.entries.size;
89
+ }
90
+ /** 丢掉最旧的直到双双回到上限内。 */
91
+ evict() {
92
+ while (this.order.length > 0
93
+ && (this.entries.size > this.opts.maxEntries || this.bytes > this.opts.maxBytes)) {
94
+ const oldest = this.order.shift();
95
+ if (oldest === undefined)
96
+ break;
97
+ const removed = this.entries.get(oldest);
98
+ this.entries.delete(oldest);
99
+ this.bytes -= approxBytes(removed?.trace);
100
+ if (this.bytes < 0)
101
+ this.bytes = 0;
102
+ }
103
+ }
104
+ }
105
+ /* ── trace id ───────────────────────────────────────────────────────────── */
106
+ /** trace id 的进程内计数器。 */
107
+ let traceSeq = 0;
108
+ /**
109
+ * §7.133: 生成 trace id。
110
+ *
111
+ * 单调计数 + 时间戳 —— 同一进程内唯一,且排序有意义(面板按时间排)。
112
+ */
113
+ export function formatTraceId(seq, ts = Date.now()) {
114
+ return `t${ts.toString(36)}-${seq.toString(36)}`;
115
+ }
116
+ /** §7.133: 取下一个 trace id(进程内单调)。 */
117
+ export function nextTraceId() {
118
+ return formatTraceId(traceSeq++);
119
+ }
120
+ /* ── call id(REQ/RESP 配对键)────────────────────────────────────────────── */
121
+ /** call id 的进程内计数器。 */
122
+ let callSeq = 0;
123
+ /**
124
+ * §7.134: 生成 call id —— **一次 LLM 往返**的配对键。
125
+ *
126
+ * ## 为什么需要它
127
+ *
128
+ * §7.133 的索引行里,REQ 与 RESP 各自有独立的 `traceId`,于是面板上一次
129
+ * 调用显示成**两行不相干的条目** —— 主人在 §7.134 里要求「请求和响应成对
130
+ * 显示,上面是请求下面是响应」。配对需要一个跨 REQ/RESP 的共同键。
131
+ *
132
+ * ## 键的粒度:一次 `stream()` = 一个 call
133
+ *
134
+ * - local adapter:一次 `stream()` 一发一收,天然一对。
135
+ * - cloud adapter(直连):`traceRequest` 在**重试循环之前**只调一次,
136
+ * 而 `traceResponse` 有三个出口(network error / HTTP error / 成功)。
137
+ * 重试时可能出现「一次失败 RESP + 一次成功 RESP」—— 它们共用同一个
138
+ * callId,面板取**最后一个** RESP(那才是这次调用的最终结果)。
139
+ * - jev(裸 fetch):REQ/RESP 在同一个方法内。
140
+ *
141
+ * ## 用 `options` 对象身份当键
142
+ *
143
+ * 宿主每次 `stream()` 传一个新的 `options` 对象,而 REQ 与 RESP 拿到的是
144
+ * **同一个**对象引用 —— 所以 `WeakMap<options, callId>` 就能把两半配上,
145
+ * 且不需要改 `traceResponse` 的三个调用点的签名。
146
+ *
147
+ * `WeakMap` 不会泄漏:`options` 被回收,条目随之消失。
148
+ */
149
+ const callIds = new WeakMap();
150
+ /** §7.134: 取一个新的 call id(进程内单调)。 */
151
+ export function nextCallId() {
152
+ return `c${callSeq++}`;
153
+ }
154
+ /**
155
+ * §7.134: 取这次调用的 call id(第一次调用时创建,之后复用)。
156
+ *
157
+ * 传入的不是对象时(理论上不会发生)退化成「每次一个新 call id」——
158
+ * 宁可配不上,也不把两个不相干的调用错配成一对。
159
+ */
160
+ export function callIdFor(options) {
161
+ if (options === null || typeof options !== 'object') {
162
+ return nextCallId();
163
+ }
164
+ const existing = callIds.get(options);
165
+ if (existing !== undefined)
166
+ return existing;
167
+ const id = nextCallId();
168
+ callIds.set(options, id);
169
+ return id;
170
+ }
171
+ /** 进程内唯一的 store 实例。 */
172
+ let singleton;
173
+ /** §7.133: 取进程内的 trace store(不存在则创建)。 */
174
+ export function getTraceStore() {
175
+ if (singleton === undefined)
176
+ singleton = new LlmTraceStore();
177
+ return singleton;
178
+ }
179
+ /**
180
+ * 把 store 注册成 cordis service(名字固定为 `llmTraceStore`)。
181
+ *
182
+ * 独立于 plugin 的 apply() —— 由**首个使用者**惰性创建并注册,这样
183
+ * 不需要在 cordis.patch.yml 里加一行(那个 patch row 的形状门禁很严)。
184
+ * 注册是 best-effort:ctx 不是 plugin-level proxy 时静默跳过,写入方照常
185
+ * 用模块单例。
186
+ */
187
+ export function provideTraceStore(ctx) {
188
+ const store = getTraceStore();
189
+ const c = ctx;
190
+ try {
191
+ const existing = typeof c.get === 'function' ? c.get('llmTraceStore') : undefined;
192
+ if (existing !== undefined && existing !== null)
193
+ return existing;
194
+ }
195
+ catch { /* not provided yet — fall through to register */ }
196
+ try {
197
+ if (typeof c.provide === 'function')
198
+ c.provide('llmTraceStore', store);
199
+ }
200
+ catch {
201
+ // 提供失败(ctx 不是 plugin-level proxy)→ 调用方降级,trace 正文不存,
202
+ // 但索引行与 ops log 照旧(面板仍能看到「调了几次、多大、多久」)。
203
+ }
204
+ return store;
205
+ }
@@ -0,0 +1,450 @@
1
+ /**
2
+ * llm-trace — 详细的 LLM request / response 追踪,写进 ops log。
3
+ *
4
+ * ## 为什么需要这个模块
5
+ *
6
+ * 现状(§7.118, 2026-10-26):adapters 只在 ops log 里记**长度**——
7
+ * `promptLen=121853 systemLen=225 resultLen=5006`。真正想看"到底发了什么、
8
+ * 回了什么"的时候,唯一去处是把 `DSH_EDGE_LOG_LEVEL` 降到 `debug`,而
9
+ * `LLM_MESSAGES` / `LLM_RESPONSE_TEXT` 那两条全量 dump 才会出现:
10
+ *
11
+ * - 全量 dump 没有脱敏 → 用户原文、工具回显、可能夹带的凭据直接落盘;
12
+ * - 全量 dump 没有截断 → 一条 120KB 的 prompt 能撑爆一行日志;
13
+ * - `debug` 级别默认被压制 → 运维默认看不到,而需要看的人往往是在出事之后。
14
+ *
15
+ * 这个模块把"详细"拆成三个**可独立关闭**的维度,并给每一维加上硬边界:
16
+ *
17
+ * 1. **脱敏**(`redactPii` / `redactSecrets`)——PII 走 `pii-utils` 的
18
+ * 10 类规则,凭据走本模块自己的密钥掩码(`pii-utils` 的 api-key 规则
19
+ * 匹配不到 `sk-cp-` / `tvly-` 这类带前缀的厂商密钥,见
20
+ * `SECRET_PATTERNS`)。
21
+ * 2. **截断**(`maxChars` / `maxMessages`)——每个字段独立上限,且**截断
22
+ * 必须显式可见**(`truncated: true` + `…[+N chars]`),绝不让"日志里
23
+ * 看起来是完整的"成为可能。
24
+ * 3. **分级**(`includeRequest` / `includeResponse`)——只要 request 或
25
+ * 只要 response,各自可关。
26
+ *
27
+ * ## 落盘位置
28
+ *
29
+ * 走 `ctx.logger.info` —— 即 ops log 的默认级别。**不是** debug:运维在
30
+ * 出事后 tail 的是 ops log,不是 debug 日志。
31
+ *
32
+ * ## 安全边界
33
+ *
34
+ * - 本模块**不**写明文密钥:`authorization` 头、`apiKey` 参数一律不进 trace。
35
+ * - 本模块**不**改变任何出网行为:它只在本地组装一个日志对象。
36
+ * - 本模块对畸形输入**永不抛错**:trace 是观测手段,不能把主链路带崩。
37
+ *
38
+ * @module dsh-edge-agent/llm-trace
39
+ */
40
+ import type { EdgeConfig } from './edge-config.js';
41
+ import type { StoredTrace, TraceCaller } from './llm-trace-store.js';
42
+ /**
43
+ * LLM trace 的行为开关。落在 `config/edge.json` 的 `audit.llmTrace` 段
44
+ * (§7.102「只有一个配置文件」——bundle patch 不再读 process.env)。
45
+ */
46
+ export interface LlmTraceConfig {
47
+ /** 总开关。false 时 adapters 完全不构造 trace。 */
48
+ enabled: boolean;
49
+ /** 是否记录 request(messages + system + 参数)。 */
50
+ includeRequest: boolean;
51
+ /** 是否记录 response(content + tool calls + finish reason)。 */
52
+ includeResponse: boolean;
53
+ /** 单个文本字段的最大保留字符数。0 = 不截断(不推荐)。 */
54
+ maxChars: number;
55
+ /** request 里最多保留多少条 message。 */
56
+ maxMessages: number;
57
+ /**
58
+ * §7.144: **完整请求体 / 响应体**快照的字符上限。
59
+ *
60
+ * 为什么要单独一个上限:`maxChars` 是给「单条 message 正文」用的(2000 足够看清
61
+ * 上下文),而 body 快照装的是**真正发出去的那一坨**(system + 全部 message +
62
+ * 工具表 + 参数;jev 那种还有整份分类体系)。主对话实测 promptLength 22.5 万
63
+ * 字符,按 2000 截就只剩一句话 —— 面板上看到的与实际发出去的对不上。
64
+ *
65
+ * 20000 字符 ≈ 20KB/条,store 的 64 条 / 2MB 双上限仍然成立。超出时**显式标注**
66
+ * 截断了多少(`truncated: true` + `…[+N chars]`),绝不假装完整。
67
+ */
68
+ maxBodyChars: number;
69
+ /** 是否做 PII 脱敏(pii-utils 的 10 类规则)。 */
70
+ redactPii: boolean;
71
+ /** 是否做凭据掩码(本模块的 SECRET_PATTERNS)。 */
72
+ redactSecrets: boolean;
73
+ }
74
+ /**
75
+ * 出厂默认。
76
+ *
77
+ * `maxChars=2000` 的取舍:一条 message 的正文通常在几百字,2000 足够看清
78
+ * 上下文;整轮 12 条 message × 2000 字 ≈ 24KB 一行,日志文件按 50MB 轮转,
79
+ * 完全可控。想看全量请走 `DSH_EDGE_LOG_LEVEL=debug` 的旧通道,不要把这个值
80
+ * 调到很大 —— 那是拿磁盘换眼睛。
81
+ */
82
+ export declare const DEFAULT_LLM_TRACE_CONFIG: LlmTraceConfig;
83
+ /**
84
+ * 从 `config/edge.json` 解析 trace 配置。
85
+ *
86
+ * 缺省逐字段回落到 `DEFAULT_LLM_TRACE_CONFIG`;`maxChars<=0` 视为"不截断"
87
+ * 但会在 trace 里标注 `truncated:false`,让读日志的人知道这是显式选择。
88
+ */
89
+ export declare function resolveLlmTraceConfig(cfg?: EdgeConfig): LlmTraceConfig;
90
+ /**
91
+ * 凭据字面量掩码(不依赖 pii-utils)。
92
+ *
93
+ * `pii-utils` 的 `api-key` 规则是
94
+ * `sk-(?:proj-|ant-|live-|test-|org-)?[a-zA-Z0-9]{20,}`,它**匹配不到**
95
+ * 厂商自定义前缀的密钥:`sk-cp-…`(MiniMax)在 `cp-` 处断了,且下一段含
96
+ * `_` 也不在字符类里;`tvly-…`(Tavily)、`sk-ws-…`(DashScope)同理。
97
+ * 实测 `config/edge.json` 里的三把真 key 全部逃逸。
98
+ *
99
+ * 所以这里独立覆盖"厂商前缀 + 通用形态"两类,并加两条**收敛条件**避免误伤
100
+ * 普通英文词:
101
+ *
102
+ * - 厂商前缀密钥要求"长度 ≥16 **且** 至少含一个数字"。真密钥几乎必然带
103
+ * 数字;而 `key-representation`、`api-key-description` 这类正常词不含
104
+ * 数字,不会被误掩。字符类放宽到 `[A-Za-z0-9._-]`(含 `.`),否则
105
+ * DashScope 的 `sk-ws-EXAMPLE0.dot0.…`(JWS 风格带点)会在第一个
106
+ * 点处断掉而逃逸。
107
+ * - 另加一条全大写形态(`tvly-PLACEHOLDER_REPLACE_ME`),这类占位符不含
108
+ * 数字但仍是凭据位。
109
+ */
110
+ export declare const SECRET_PATTERNS: Array<{
111
+ name: string;
112
+ pattern: RegExp;
113
+ }>;
114
+ /** 掩码后的固定占位符。 */
115
+ export declare const SECRET_MASK = "[REDACTED]";
116
+ export interface SecretMaskResult {
117
+ text: string;
118
+ /** 命中的规则名(去重)。 */
119
+ hits: string[];
120
+ }
121
+ /**
122
+ * 把文本里的凭据字面量替换成 `SECRET_MASK`。
123
+ *
124
+ * 顺序有意义:先打标签式(`apiKey=…`),再打 `Bearer`,最后打裸厂商前缀 ——
125
+ * 否则 `Bearer sk-cp-xxx` 会被前两条先吃掉,留下一截尾巴。
126
+ */
127
+ export declare function maskSecrets(text: string): SecretMaskResult;
128
+ export interface TraceText {
129
+ /** 实际写入日志的文本。 */
130
+ text: string;
131
+ /** 截断前的原始长度。 */
132
+ length: number;
133
+ /** 是否发生了截断。 */
134
+ truncated: boolean;
135
+ }
136
+ /**
137
+ * 按字符数截断,**显式标注**溢出了多少。
138
+ *
139
+ * 不用 `…` 单字符收尾 —— 它和原文里的省略号没法区分。统一用
140
+ * `…[+N chars]`,`grep` 得到的就是真的。
141
+ */
142
+ export declare function truncateForLog(text: string, maxChars: number): TraceText;
143
+ /**
144
+ * 对一段要进日志的文本走完整条链:凭据掩码 → PII 脱敏 → 截断。
145
+ *
146
+ * 顺序同样有讲究:先掩码再脱敏,避免 PII 规则把密钥的一部分改写成别的形态
147
+ * 而逃过掩码;截断放最后,保证掩码与脱敏作用在**完整**文本上(否则密钥可能
148
+ * 恰好落在被切掉的那一段里……不对,那样反而安全了,但 PII 的部分掩码会因
149
+ * 截断而失配。结论:先处理完再截,语义最直观)。
150
+ */
151
+ export declare function sanitizeTraceText(text: string, cfg: LlmTraceConfig): TraceText;
152
+ /**
153
+ * 把任意 body 序列化成**可安全记录**的文本。
154
+ *
155
+ * 三个坑:
156
+ * - **循环引用** → `JSON.stringify` 直接抛 `TypeError`。body 里出现循环
157
+ * (宿主 options 被塞回 body 这类)就会把观测路径变成故障源。
158
+ * - **BigInt** → 同样抛。
159
+ * - **不可序列化的值**(函数 / Symbol)→ `JSON.stringify` 会静默丢掉,
160
+ * 让人以为"没发这个字段"。用 replacer 把它们替换成占位符,**显式**告诉
161
+ * 读日志的人「这里有个记不住的值」,而不是让它凭空消失。
162
+ */
163
+ export declare function serializeTraceBody(value: unknown): string;
164
+ /**
165
+ * body 快照的脱敏 + 截断。
166
+ *
167
+ * 走与正文同一条链(凭据掩码 → PII 脱敏 → 截断),只是上限换成
168
+ * `maxBodyChars`(body 比单条 message 大得多,按 2000 截就没意义了)。
169
+ */
170
+ export declare function buildBodySnapshot(value: unknown, config: LlmTraceConfig): TraceText | undefined;
171
+ /** 一条 message 的 trace 视图。 */
172
+ export interface LlmMessageTrace {
173
+ index: number;
174
+ role: string;
175
+ /** `source.kind`(`tool` / `user` / …),能区分"工具回显"和"用户输入"。 */
176
+ kind?: string;
177
+ /**
178
+ * §7.131: `source.provider` —— 产出这条 assistant 消息的 provider 路由。
179
+ * 出网裁剪(cloud-egress)按它把会话历史切成「云段 / 本地段」,所以把它
180
+ * 也透出到 trace 里:operator 一眼能看出「这段历史是本地池的还是云端的」。
181
+ */
182
+ provider?: string;
183
+ /** 正文原始长度。 */
184
+ length: number;
185
+ /** 正文(已脱敏 + 截断)。 */
186
+ text?: TraceText;
187
+ }
188
+ export interface LlmRequestTrace {
189
+ sessionId: string;
190
+ provider: string;
191
+ model: string;
192
+ baseUrl?: string;
193
+ /** 实际发出的 message 条数(不是 trace 里保留的条数)。 */
194
+ messageCount: number;
195
+ /** trace 里因 maxMessages 截掉了几条。 */
196
+ droppedMessages: number;
197
+ /** 全部 message 正文的总长度(未截断前)。 */
198
+ promptLength: number;
199
+ system?: TraceText;
200
+ messages: LlmMessageTrace[];
201
+ /** 本次请求带的工具名(不含 schema)。 */
202
+ tools?: string[];
203
+ /**
204
+ * §7.144: **真正发出去的那一坨**(完整请求体的 JSON 快照)。
205
+ *
206
+ * 为什么必须有它:上面的 `system` / `messages` / `tools` 只是请求体的**投影**,
207
+ * 而实际 body 里还有投影装不下的东西 —— jev 的整份分类体系(questions.*)、
208
+ * adapter 的 `chat_template_kwargs` / `reasoning_effort` / 工具 schema ……
209
+ * 主人现场问的就是这个:「request 里只发了那段 system 吗?」(答案:不是,
210
+ * 但 trace 当时只**记**了那两段,面板上看不出真相)。
211
+ *
212
+ * 脱敏 + 截断照旧(凭据 → PII → 截断),上限独立(`maxBodyChars`)。
213
+ */
214
+ body?: TraceText;
215
+ params?: {
216
+ temperature?: number;
217
+ maxTokens?: number;
218
+ stream?: boolean;
219
+ };
220
+ }
221
+ /** 构造 request trace 时的输入。 */
222
+ export interface LlmRequestTraceInput {
223
+ sessionId: string;
224
+ provider: string;
225
+ model: string;
226
+ baseUrl?: string;
227
+ system?: string;
228
+ messages: Array<{
229
+ role: string;
230
+ content: unknown;
231
+ source?: {
232
+ kind?: string;
233
+ provider?: string;
234
+ };
235
+ }>;
236
+ tools?: Array<{
237
+ name?: string;
238
+ function?: {
239
+ name?: string;
240
+ };
241
+ }>;
242
+ /**
243
+ * §7.144: **实际发出的请求体**(原样对象)—— 记进 `trace.body`。
244
+ *
245
+ * 传字符串时按「已经序列化好的正文」处理(jev 的裸 fetch 直接给 body 对象,
246
+ * 而某些路径手里只有一段文本)。
247
+ */
248
+ rawBody?: unknown;
249
+ params?: LlmRequestTrace['params'];
250
+ config: LlmTraceConfig;
251
+ }
252
+ /**
253
+ * 把 `options.messages` 的 content 摊平成纯文本。
254
+ *
255
+ * dsh-llm 的 `Message.content` 有两种形态:content-block 数组(`{type:'text',
256
+ * text}`)和裸字符串。两种都要认,否则一半消息在 trace 里是空的。
257
+ */
258
+ export declare function messageContentToText(content: unknown): string;
259
+ /**
260
+ * 构造 request trace。
261
+ *
262
+ * 保留**最后** `maxMessages` 条 message(而不是前 `maxMessages` 条):多轮
263
+ * 对话里真正决定这次请求的是最近几轮,开头的 system 指令已经被压进
264
+ * `system` 字段了。
265
+ */
266
+ export declare function buildRequestTrace(input: LlmRequestTraceInput): LlmRequestTrace;
267
+ export interface LlmToolCallTrace {
268
+ id?: string;
269
+ name: string;
270
+ arguments?: TraceText;
271
+ }
272
+ export interface LlmResponseTrace {
273
+ sessionId: string;
274
+ provider: string;
275
+ model: string;
276
+ /** 第几次尝试(重试计数从 1 开始)。 */
277
+ attempt?: number;
278
+ durationMs?: number;
279
+ finishReason?: string;
280
+ /** 助手正文(已脱敏 + 截断)。 */
281
+ content?: TraceText;
282
+ toolCalls: LlmToolCallTrace[];
283
+ /**
284
+ * §7.144: **完整响应体**快照(上游真正回来的东西)。
285
+ *
286
+ * `content` + `toolCalls` 是响应体的**投影**;投影装不下的部分(jev 的
287
+ * `usage` / `answers` 全量结构、上游的原始错误体、SSE 帧里的额外字段)
288
+ * 只有完整 body 才有。主人要「请求和响应记录完全」,这一栏就是那个"完全"。
289
+ */
290
+ body?: TraceText;
291
+ usage?: {
292
+ promptTokens?: number;
293
+ completionTokens?: number;
294
+ totalTokens?: number;
295
+ };
296
+ /** 上游错误信息(已截断)。 */
297
+ error?: string;
298
+ }
299
+ export interface LlmResponseTraceInput {
300
+ sessionId: string;
301
+ provider: string;
302
+ model: string;
303
+ attempt?: number;
304
+ durationMs?: number;
305
+ finishReason?: string;
306
+ content?: string;
307
+ toolCalls?: Array<{
308
+ id?: string;
309
+ name: string;
310
+ arguments?: string;
311
+ }>;
312
+ /**
313
+ * §7.144: **完整响应体**(原样对象或已经序列化好的文本)。
314
+ *
315
+ * jev 的响应是一整段 JSON(`answers` / `usage` 都在里面),adapter 的响应
316
+ * 是「正文 + tool_calls + finish_reason」的组装体 —— 两者都传进来,面板上
317
+ * 才看得到完整往返。
318
+ */
319
+ rawBody?: unknown;
320
+ usage?: LlmResponseTrace['usage'];
321
+ error?: string;
322
+ config: LlmTraceConfig;
323
+ }
324
+ /** 构造 response trace。 */
325
+ export declare function buildResponseTrace(input: LlmResponseTraceInput): LlmResponseTrace;
326
+ /**
327
+ * 把 trace 压成**单行**字符串。
328
+ *
329
+ * ops log 的 `formatLine` 会把换行折成 `⏎`,所以这里自己保证单行:
330
+ * `JSON.stringify` 已经把字符串内的换行转义成 `\n`,但仍可能出现裸的
331
+ * 控制字符(U+2028/2029 等),统一替换掉,避免日志文件出现"一行日志占了
332
+ * 三行物理行"的破事。
333
+ */
334
+ export declare function formatTraceLine(tag: string, trace: unknown): string;
335
+ /**
336
+ * §7.133: trace 的**索引行**(进 Edge 面板)+ **正文**(按需拉取)。
337
+ *
338
+ * §7.118 的 trace 只走 `ctx.logger.info` → ops log 文件,而 Edge 面板的
339
+ * 数据源是 audit-log —— 两条通路不相通,面板看不到任何 request/response。
340
+ *
341
+ * 这里把一次调用拆成两半:
342
+ *
343
+ * - **索引行**(`llm.trace.index`):几十字节的标量摘要,进 audit 行,
344
+ * 于是面板能列出「这一轮调了哪几次 LLM、各自多大、多久」。
345
+ * - **正文**:完整的 trace(12 条 message 的正文 / response 的正文与
346
+ * tool calls),存在 `LlmTraceStore` 里,点开才取。
347
+ *
348
+ * 为什么不把正文直接塞进 audit 行:单轮主对话的 request 正文实测 **22.5 万
349
+ * 字符**,几十轮后面板常驻内存线性膨胀。索引 + 按需拉取是本仓一贯的
350
+ * 纪律(§7.107 / §7.129:宁可漏显示,也不能显示一件没发生过的事)。
351
+ */
352
+ /** 索引行的 payload(全部是标量,可安全进面板)。 */
353
+ export interface TraceIndexPayload {
354
+ /** 与 store 里的正文对应。 */
355
+ traceId: string;
356
+ /** REQ = 请求,RESP = 响应。 */
357
+ kind: 'REQ' | 'RESP';
358
+ provider: string;
359
+ model: string;
360
+ /**
361
+ * §7.147:这次调用属于哪个会话。
362
+ *
363
+ * 没有就是**没有这个键** —— 索引行于是落全局桶(面板标它「全局信号」)。
364
+ * 载荷里带 sessionId 才是会话行(`turn >= 0`),路由面板才能说「这一轮」。
365
+ * 实测:本仓 195 条 `llm.trace.index` **无一带** sessionId ⇒ 路由面板此前
366
+ * 结构上不可能归属任何一轮。
367
+ */
368
+ sessionId?: string;
369
+ /** §7.132:内部调用标记;undefined = 主对话出网。 */
370
+ caller?: string;
371
+ /** 请求:实际发出的 message 条数 / 正文总长 / 因 maxMessages 丢掉几条。 */
372
+ messageCount?: number;
373
+ droppedMessages?: number;
374
+ promptLength?: number;
375
+ /** 响应:助手正文长度 / tool call 数 / 用时 / finish reason。 */
376
+ contentLength?: number;
377
+ toolCallCount?: number;
378
+ durationMs?: number;
379
+ finishReason?: string;
380
+ /** 出错时的原因。 */
381
+ error?: string;
382
+ /**
383
+ * 本次请求带的工具数。
384
+ *
385
+ * §7.132 的判据:主对话恒带 30+ 件工具,一切内部调用(安检 / 分类器 /
386
+ * 宿主会话标题生成)**一件都不带**。索引行带上这个数,面板就能把
387
+ * 「没有 caller 标记、也没带工具」的调用认成**宿主的内部调用**而不是
388
+ * 冒充主对话出网。
389
+ */
390
+ toolCount?: number;
391
+ /** 本条 trace 的正文总字节(面板可据此提示「点开看 N KB」)。 */
392
+ bodyBytes?: number;
393
+ /** 底座 URL —— 判断这一轮发去了哪。 */
394
+ baseUrl?: string;
395
+ /**
396
+ * §7.134: **一次 LLM 往返**的配对键。
397
+ *
398
+ * REQ 与 RESP 各有自己的 `traceId`(正文存储的键),但同一次往返的两半
399
+ * 共用这个 `callId` —— 面板据此把「请求 / 响应」配成一对,上下排列。
400
+ */
401
+ callId?: string;
402
+ }
403
+ /**
404
+ * 构造索引行 payload。
405
+ *
406
+ * 从 trace 对象里**提取标量**——不复制正文。正文另存 store。
407
+ */
408
+ export declare function buildTraceIndex(traceId: string, kind: 'REQ' | 'RESP', trace: unknown, caller?: string, extra?: {
409
+ toolCount?: number;
410
+ callId?: string;
411
+ }): TraceIndexPayload;
412
+ /** emitTrace 需要的最小依赖面。 */
413
+ export interface TraceSink {
414
+ /** audit-log 的 log() —— 索引行走这里。 */
415
+ log(entry: {
416
+ level: 'debug' | 'info' | 'warn' | 'error' | 'security';
417
+ kind: string;
418
+ source: string;
419
+ payload: Record<string, unknown>;
420
+ }): Promise<unknown>;
421
+ /** 正文存储 —— 正文走这里(可缺省:那就只有 ops log 的老通路)。 */
422
+ store?: {
423
+ put(id: string, entry: StoredTrace): void;
424
+ };
425
+ /** ops log 的 logger —— 保持 §7.118 的老通路不变。 */
426
+ logger?: {
427
+ info?: (msg: string) => void;
428
+ };
429
+ /** 生成 trace id(测试可注入固定值)。 */
430
+ nextTraceId?: () => string;
431
+ }
432
+ /**
433
+ * §7.133: 一次 LLM 调用的**统一出口**。
434
+ *
435
+ * 三条通路同时写:
436
+ * 1. ops log(§7.118 的老行为,`LLM_REQUEST` / `LLM_RESPONSE` 单行)
437
+ * 2. audit 索引行(`llm.trace.index`)→ Edge 面板列出这次调用
438
+ * 3. 正文 store(`LlmTraceStore`)→ 面板点开时取
439
+ *
440
+ * 检测调用(安检 / 分类器)经 `caller` 标出来 —— §7.132 的
441
+ * `edgeInternalCaller` 是同一个标记体系。**标记的来源是 options**:
442
+ * adapter 调 `emitTrace` 时把 `options` 传进来,由 `internalCallerOf()`
443
+ * 读出标记。所以检测调用的 trace 与主对话的 trace 走的是**同一段代码**,
444
+ * 不存在「检测调用忘了记」的第二套实现。
445
+ */
446
+ export declare function emitTrace(sink: TraceSink, kind: 'REQ' | 'RESP', tag: string, trace: unknown, caller?: TraceCaller, extra?: {
447
+ toolCount?: number;
448
+ callId?: string;
449
+ }): string | undefined;
450
+ //# sourceMappingURL=llm-trace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-trace.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/llm-trace.ts"],"names":[],"mappings":"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"}