fix(telemetry): isolate collector transport and address review
This commit is contained in:
1 parent
8b89200324
commit
535cb4aae3
31 files changed
+289
-153
No files matched your search
@@ -57,6 +57,7 @@ External packages installed for runtime use or distributed inside the prebuilt b
|
||||
| [`@opentelemetry/core`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
|
||||
| [`@opentelemetry/exporter-logs-otlp-http`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
|
||||
| [`@opentelemetry/otlp-exporter-base`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
|
||||
| [`@opentelemetry/otlp-transformer`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
|
||||
| [`@opentelemetry/resources`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
|
||||
| [`@opentelemetry/sdk-logs`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
|
||||
| [`@playwright/mcp`](https://github.com/microsoft/playwright-mcp) | Apache-2.0 |
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/capability-seams.md
|
||||
capability-seams.md: d9232c12528c274f48b85e725b944e565a5431de
|
||||
capability-seams.zh.md: 81e6aabc5351b9a0827fbd544e46d704688c9338
|
||||
capability-seams.md: 072cb47af420136a08ce182cfa341bb42fc068e0
|
||||
capability-seams.zh.md: 138fb3241a00cb2f6ab8853dd9cbf1440fe6d490
|
||||
@@ -3,12 +3,10 @@
|
||||
|
||||
# Capability Seams And Core Services
|
||||
|
||||
A service can be a core spine service, a swappable capability seam, or a bundle/composition point. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.
|
||||
A service can be a core spine service, a swappable capability seam, a bundle/composition point, or a standalone service. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
pkg_product_telemetry_otel["product-telemetry-otel"]
|
||||
svc_productTelemetry["ctx.productTelemetry<br/>Product usage event sender"]
|
||||
pkg_hmr["hmr"]
|
||||
svc_hmr["ctx.hmr<br/>Serialized module and configuration reloads"]
|
||||
pkg_app_boot["app-boot"]
|
||||
@@ -106,6 +104,8 @@ flowchart LR
|
||||
pkg_credentials_local["credentials-local"]
|
||||
pkg_authorization["authorization"]
|
||||
svc_authorization["ctx.authorization<br/>Authorization flow registry"]
|
||||
pkg_host_product_telemetry_otel["host-product-telemetry-otel"]
|
||||
svc_productTelemetry["ctx.productTelemetry<br/>Product usage event sender"]
|
||||
pkg_session_telemetry["session-telemetry"]
|
||||
svc_sessionTelemetry["ctx.sessionTelemetry<br/>Session telemetry seam"]
|
||||
pkg_session_telemetry_otel["session-telemetry-otel"]
|
||||
@@ -320,6 +320,7 @@ flowchart LR
|
||||
pkg_host_directory_picker --> svc_directoryPicker
|
||||
pkg_host_directory_picker_browse --> svc_directoryPicker
|
||||
pkg_host_directory_picker_native --> svc_directoryPicker
|
||||
pkg_host_product_telemetry_otel --> svc_productTelemetry
|
||||
pkg_host_webserver --> svc_webServer
|
||||
pkg_inspector --> svc_inspector
|
||||
pkg_invariants --> svc_invariants
|
||||
@@ -339,7 +340,6 @@ flowchart LR
|
||||
pkg_plan_mode --> svc_planMode
|
||||
pkg_plugin_manager --> svc_pluginManager
|
||||
pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions
|
||||
pkg_product_telemetry_otel --> svc_productTelemetry
|
||||
pkg_ptc_runtime --> svc_ptcRuntime
|
||||
pkg_ptc_runtime_node --> svc_ptcRuntime
|
||||
pkg_pwsh_local --> svc_shell
|
||||
@@ -554,7 +554,6 @@ flowchart LR
|
||||
|
||||
| ctx key | Role | Owner | Implementations | Direct consumers | Companion plugins | Note |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `ctx.productTelemetry` | `core` | [`product-telemetry-otel`](../packages/host/product-telemetry-otel) | - | - | - | Exports explicitly submitted analytics events through OTLP/HTTP; mounting alone collects nothing. |
|
||||
| `ctx.hmr` | `core` | [`hmr`](../packages/boot/hmr) | - | [`app-boot`](../packages/boot/app-boot) | - | Owns module and exact configuration watchers; application mutations share its queue and automatic reloads await the application file lock. |
|
||||
| `ctx.pluginManager` | `core` | [`plugin-manager`](../packages/boot/plugin-manager) | - | [`plugin-manager`](../packages/boot/plugin-manager), `ui-settings-plugin-inventory` | - | Shares profile package operations with the CLI and reports persisted and running state to Web and agent callers. |
|
||||
| `ctx.profileContext` | `core` | [`app-boot`](../packages/boot/app-boot) | - | [`plugin-manager`](../packages/boot/plugin-manager) | - | The dsh launcher supplies data-only profile locations and composition inputs; reload scheduling belongs to dsh-hmr. |
|
||||
@@ -590,6 +589,7 @@ flowchart LR
|
||||
| `ctx.subagentModelSelection` | `core` | [`tool-subagent`](../packages/subagent/tool-subagent) | - | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Owns the default-off settings namespace that Agent-scoped delegation tools sample when composing a new top-level Session. |
|
||||
| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`api-settings-controller`](../packages/api/settings-controller), [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | Configuration carries references to secrets; providers own the values. Consumers resolve per operation, so a rotated credential reaches the very next request; the settings controller exposes value-free views and write-only storage. |
|
||||
| `ctx.authorization` | `seam` | [`authorization`](../packages/credentials/authorization) | - | [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | Flows are registered by the plugin that knows how to obtain one credential and keyed by the record they write; the seam owns the conversation and the one-attempt-per-key lifecycle, never the protocol. |
|
||||
| `ctx.productTelemetry` | `service` | [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | - | - | - | Exports explicitly submitted analytics events through OTLP/HTTP; mounting alone collects nothing. |
|
||||
| `ctx.sessionTelemetry` | `seam` | [`session-telemetry`](../packages/session/session-telemetry) | [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | - | The seam captures, redacts, and hands session records to one backend; nothing else consumes the service — its output leaves the process. |
|
||||
| `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | Backends register side by side under names; data forms (domain first) mount on the hub and translate typed operations into opaque KV-unit primitives. |
|
||||
| `ctx.storageDomain` | `core` | [`storage-domain`](../packages/storage/storage-domain) | - | [`workspace`](../packages/workspace/workspace) | - | Waits for every configured backend, then publishes the domain form as one lifecycle-bound service for typed durable state. |
|
||||
|
||||
@@ -5,12 +5,10 @@
|
||||
|
||||
[English](capability-seams.md) | 中文
|
||||
|
||||
服务可以是核心主干服务、可替换的能力 seam,也可以是组合包/组合点。下图展示了拥有服务声明的包、已知实现包,以及直接消费该服务的包。
|
||||
服务可以是核心主干服务、可替换的能力 seam、组合包/组合点或独立服务。下图展示了拥有服务声明的包、已知实现包,以及直接消费该服务的包。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
pkg_product_telemetry_otel["product-telemetry-otel"]
|
||||
svc_productTelemetry["ctx.productTelemetry<br/>Product usage event sender"]
|
||||
pkg_hmr["hmr"]
|
||||
svc_hmr["ctx.hmr<br/>Serialized module and configuration reloads"]
|
||||
pkg_app_boot["app-boot"]
|
||||
@@ -108,6 +106,8 @@ flowchart LR
|
||||
pkg_credentials_local["credentials-local"]
|
||||
pkg_authorization["authorization"]
|
||||
svc_authorization["ctx.authorization<br/>Authorization flow registry"]
|
||||
pkg_host_product_telemetry_otel["host-product-telemetry-otel"]
|
||||
svc_productTelemetry["ctx.productTelemetry<br/>Product usage event sender"]
|
||||
pkg_session_telemetry["session-telemetry"]
|
||||
svc_sessionTelemetry["ctx.sessionTelemetry<br/>Session telemetry seam"]
|
||||
pkg_session_telemetry_otel["session-telemetry-otel"]
|
||||
@@ -322,6 +322,7 @@ flowchart LR
|
||||
pkg_host_directory_picker --> svc_directoryPicker
|
||||
pkg_host_directory_picker_browse --> svc_directoryPicker
|
||||
pkg_host_directory_picker_native --> svc_directoryPicker
|
||||
pkg_host_product_telemetry_otel --> svc_productTelemetry
|
||||
pkg_host_webserver --> svc_webServer
|
||||
pkg_inspector --> svc_inspector
|
||||
pkg_invariants --> svc_invariants
|
||||
@@ -341,7 +342,6 @@ flowchart LR
|
||||
pkg_plan_mode --> svc_planMode
|
||||
pkg_plugin_manager --> svc_pluginManager
|
||||
pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions
|
||||
pkg_product_telemetry_otel --> svc_productTelemetry
|
||||
pkg_ptc_runtime --> svc_ptcRuntime
|
||||
pkg_ptc_runtime_node --> svc_ptcRuntime
|
||||
pkg_pwsh_local --> svc_shell
|
||||
@@ -556,7 +556,6 @@ flowchart LR
|
||||
|
||||
| ctx 键 | 角色 | 所属包 | 实现 | 直接消费方 | 配套插件 | 说明 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `ctx.productTelemetry` | `core` | [`product-telemetry-otel`](../packages/host/product-telemetry-otel) | - | - | - | 通过 OTLP/HTTP 发送显式提交的分析事件;仅挂载插件不会采集信息。 |
|
||||
| `ctx.hmr` | `core` | [`hmr`](../packages/boot/hmr) | - | [`app-boot`](../packages/boot/app-boot) | - | 负责模块和精确配置监听;应用修改共用其队列,自动重载等待应用文件锁。 |
|
||||
| `ctx.pluginManager` | `core` | [`plugin-manager`](../packages/boot/plugin-manager) | - | [`plugin-manager`](../packages/boot/plugin-manager), `ui-settings-plugin-inventory` | - | 与 CLI 共享 profile 包操作,并向 Web 和 Agent 调用方分别报告持久状态与运行状态。 |
|
||||
| `ctx.profileContext` | `core` | [`app-boot`](../packages/boot/app-boot) | - | [`plugin-manager`](../packages/boot/plugin-manager) | - | dsh launcher 提供纯数据形式的 profile 位置与组合输入;重载调度由 dsh-hmr 负责。 |
|
||||
@@ -592,6 +591,7 @@ flowchart LR
|
||||
| `ctx.subagentModelSelection` | `core` | [`tool-subagent`](../packages/subagent/tool-subagent) | - | [`tool-subagent`](../packages/subagent/tool-subagent) | - | 拥有默认关闭的设置命名空间;Agent 作用域的委派工具会在组合新顶层 Session 时读取它。 |
|
||||
| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`api-settings-controller`](../packages/api/settings-controller), [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | 配置携带对机密信息的引用;提供方拥有实际值。消费方按操作解析,因此轮换后的凭据会在紧接着的下一次请求中生效;settings controller 提供不含实际值的视图和只写存储。 |
|
||||
| `ctx.authorization` | `seam` | [`authorization`](../packages/credentials/authorization) | - | [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | flow 由知道如何取得某份凭据的插件注册,并以其写入的记录为键;seam 拥有这段对话与"每个键同时只跑一次尝试"的生命周期,而非协议本身。 |
|
||||
| `ctx.productTelemetry` | `service` | [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | - | - | - | 通过 OTLP/HTTP 发送显式提交的分析事件;仅挂载插件不会采集信息。 |
|
||||
| `ctx.sessionTelemetry` | `seam` | [`session-telemetry`](../packages/session/session-telemetry) | [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | - | 该 seam 捕获会话记录、进行脱敏并交给一个后端;没有其他组件消费该服务,其输出会离开当前进程。 |
|
||||
| `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | 各后端以不同名称并列注册;数据形态(领域优先)挂载到枢纽上,并将类型化操作转换为不透明的 KV 单元原语。 |
|
||||
| `ctx.storageDomain` | `core` | [`storage-domain`](../packages/storage/storage-domain) | - | [`workspace`](../packages/workspace/workspace) | - | 等待所有已配置后端就绪,然后将领域形态发布为一个受生命周期约束的服务,用于类型化持久状态。 |
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
||||
config-catalog.md: 821605161613f620ca40e3a8733ee4a613fe3f95
|
||||
config-catalog.zh.md: ce811976fded14184f8edd5da5de8d70dbe71d24
|
||||
config-catalog.md: 2225721f4adf3e938da7fa74a3b1cb2b159b0ed4
|
||||
config-catalog.zh.md: 21e95ea2a3bbbcf3f4667dd3f26fc10e6f607d7b
|
||||
+34
-34
@@ -1208,6 +1208,40 @@ export interface Config {
|
||||
|
||||
Source: [`packages/host/open-in-app/src/index.ts:50`](../packages/host/open-in-app/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-host-product-telemetry-otel"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-host-product-telemetry-otel`
|
||||
|
||||
```ts config-catalog
|
||||
/** Collector routing, application identity, and bounded in-memory batch settings. */
|
||||
export interface Config {
|
||||
/** Full HTTP(S) logs URL. */
|
||||
endpoint: string
|
||||
/** Collector routing header. */
|
||||
channel: string
|
||||
/** Resource service.name supplied by the application composition. */
|
||||
serviceName: string
|
||||
/** Resource service.version supplied by the application composition. */
|
||||
serviceVersion: string
|
||||
/** Omit to honor OTEL_EXPORTER_OTLP_LOGS_COMPRESSION / OTEL_EXPORTER_OTLP_COMPRESSION. */
|
||||
compression?: 'none' | 'gzip'
|
||||
/** Maximum records per export; must not exceed maxQueueSize. */
|
||||
maxExportBatchSize: number
|
||||
/** Maximum queued records; the SDK drops new records when full. */
|
||||
maxQueueSize: number
|
||||
/** Delay before exporting a partial batch. */
|
||||
scheduledDelayMillis: number
|
||||
/** Exporter HTTP deadline, including SDK transient-error retries. */
|
||||
timeoutMillis: number
|
||||
/** Processor deadline for one batch export. */
|
||||
exportTimeoutMillis: number
|
||||
/** Outer shutdown wait; pending exports may be lost after this deadline. */
|
||||
shutdownTimeoutMillis: number
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/host/product-telemetry-otel/src/index.ts:37`](../packages/host/product-telemetry-otel/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-host-webserver"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-host-webserver`
|
||||
@@ -2049,40 +2083,6 @@ export interface Config {
|
||||
|
||||
Source: [`packages/llm/plugin-package-inventory-deepseek/src/index.ts:32`](../packages/llm/plugin-package-inventory-deepseek/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-product-telemetry-otel"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-product-telemetry-otel`
|
||||
|
||||
```ts config-catalog
|
||||
/** Collector routing, application identity, and bounded in-memory batch settings. */
|
||||
export interface Config {
|
||||
/** Full HTTP(S) logs URL. */
|
||||
endpoint: string
|
||||
/** Collector routing header. */
|
||||
channel: string
|
||||
/** Resource service.name supplied by the application composition. */
|
||||
serviceName: string
|
||||
/** Resource service.version supplied by the application composition. */
|
||||
serviceVersion: string
|
||||
/** Omit to honor OTEL_EXPORTER_OTLP_LOGS_COMPRESSION / OTEL_EXPORTER_OTLP_COMPRESSION. */
|
||||
compression?: 'none' | 'gzip'
|
||||
/** Maximum records per export; must not exceed maxQueueSize. */
|
||||
maxExportBatchSize: number
|
||||
/** Maximum queued records; the SDK drops new records when full. */
|
||||
maxQueueSize: number
|
||||
/** Delay before exporting a partial batch. */
|
||||
scheduledDelayMillis: number
|
||||
/** Exporter HTTP deadline, including SDK transient-error retries. */
|
||||
timeoutMillis: number
|
||||
/** Processor deadline for one batch export. */
|
||||
exportTimeoutMillis: number
|
||||
/** Outer shutdown wait; pending exports may be lost after this deadline. */
|
||||
shutdownTimeoutMillis: number
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/host/product-telemetry-otel/src/index.ts:35`](../packages/host/product-telemetry-otel/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-ptc-runtime-node"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-ptc-runtime-node`
|
||||
|
||||
+34
-34
@@ -1212,6 +1212,40 @@ export interface Config {
|
||||
|
||||
来源:[`packages/host/open-in-app/src/index.ts:50`](../packages/host/open-in-app/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-host-product-telemetry-otel"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-host-product-telemetry-otel`
|
||||
|
||||
```ts config-catalog
|
||||
/** Collector routing, application identity, and bounded in-memory batch settings. */
|
||||
export interface Config {
|
||||
/** Full HTTP(S) logs URL. */
|
||||
endpoint: string
|
||||
/** Collector routing header. */
|
||||
channel: string
|
||||
/** Resource service.name supplied by the application composition. */
|
||||
serviceName: string
|
||||
/** Resource service.version supplied by the application composition. */
|
||||
serviceVersion: string
|
||||
/** Omit to honor OTEL_EXPORTER_OTLP_LOGS_COMPRESSION / OTEL_EXPORTER_OTLP_COMPRESSION. */
|
||||
compression?: 'none' | 'gzip'
|
||||
/** Maximum records per export; must not exceed maxQueueSize. */
|
||||
maxExportBatchSize: number
|
||||
/** Maximum queued records; the SDK drops new records when full. */
|
||||
maxQueueSize: number
|
||||
/** Delay before exporting a partial batch. */
|
||||
scheduledDelayMillis: number
|
||||
/** Exporter HTTP deadline, including SDK transient-error retries. */
|
||||
timeoutMillis: number
|
||||
/** Processor deadline for one batch export. */
|
||||
exportTimeoutMillis: number
|
||||
/** Outer shutdown wait; pending exports may be lost after this deadline. */
|
||||
shutdownTimeoutMillis: number
|
||||
}
|
||||
```
|
||||
|
||||
来源: [`packages/host/product-telemetry-otel/src/index.ts:37`](../packages/host/product-telemetry-otel/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-host-webserver"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-host-webserver`
|
||||
@@ -2053,40 +2087,6 @@ export interface Config {
|
||||
|
||||
来源:[`packages/llm/plugin-package-inventory-deepseek/src/index.ts:32`](../packages/llm/plugin-package-inventory-deepseek/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-product-telemetry-otel"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-product-telemetry-otel`
|
||||
|
||||
```ts config-catalog
|
||||
/** Collector routing, application identity, and bounded in-memory batch settings. */
|
||||
export interface Config {
|
||||
/** Full HTTP(S) logs URL. */
|
||||
endpoint: string
|
||||
/** Collector routing header. */
|
||||
channel: string
|
||||
/** Resource service.name supplied by the application composition. */
|
||||
serviceName: string
|
||||
/** Resource service.version supplied by the application composition. */
|
||||
serviceVersion: string
|
||||
/** Omit to honor OTEL_EXPORTER_OTLP_LOGS_COMPRESSION / OTEL_EXPORTER_OTLP_COMPRESSION. */
|
||||
compression?: 'none' | 'gzip'
|
||||
/** Maximum records per export; must not exceed maxQueueSize. */
|
||||
maxExportBatchSize: number
|
||||
/** Maximum queued records; the SDK drops new records when full. */
|
||||
maxQueueSize: number
|
||||
/** Delay before exporting a partial batch. */
|
||||
scheduledDelayMillis: number
|
||||
/** Exporter HTTP deadline, including SDK transient-error retries. */
|
||||
timeoutMillis: number
|
||||
/** Processor deadline for one batch export. */
|
||||
exportTimeoutMillis: number
|
||||
/** Outer shutdown wait; pending exports may be lost after this deadline. */
|
||||
shutdownTimeoutMillis: number
|
||||
}
|
||||
```
|
||||
|
||||
来源: [`packages/host/product-telemetry-otel/src/index.ts:35`](../packages/host/product-telemetry-otel/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-ptc-runtime-node"></a>
|
||||
|
||||
## `@deepseek-ai/dsh-ptc-runtime-node`
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/README.md
|
||||
README.md: 7534586b4bd2686cd75da160fe044806afcff539
|
||||
README.zh.md: 1132d9a9e0c0bb945532020b314f87a08b84b933
|
||||
README.md: d2e8dea206e24ae65676ae50596a3f559239cc21
|
||||
README.zh.md: 78c97a016a93415ac1fca1afb6658aa623a61f7a
|
||||
@@ -6,7 +6,6 @@ One page per subsystem of the DeepSeek Harness: what it is, the data structures
|
||||
|
||||
| Page | Owns |
|
||||
|---|---|
|
||||
| [product-telemetry.md](product-telemetry.md) | Explicit product analytics submission and OTLP/HTTP transport |
|
||||
| [boot.md](boot.md) | Current-profile plugin management and launcher reload coordination |
|
||||
| [core.md](core.md) | how `packages/core` controls the agent loop: the package-by-package loop description, agent creation and ownership (`AgentHandle`), the `Agent` handle's delivery/cancellation/interception contracts, and the repo-wide type patterns (`…Map → derived-union`, branded ids) |
|
||||
| [llm-streaming.md](llm-streaming.md) | the `packages/llm` conversation types — `Message`/`ContentBlock`, the assembled model request, the `StreamChunk` wire protocol and adapter contract, `BlockAssembler`, and the `LlmAdapter` provider contract |
|
||||
@@ -68,5 +67,6 @@ One page per subsystem of the DeepSeek Harness: what it is, the data structures
|
||||
| [conversation.md](conversation.md) | target-neutral Session-event assembly: Context identity, Location data, replay paths, view builders, and target-owned render nodes |
|
||||
| [session-projection.md](session-projection.md) | the projection seam: `SessionProjectionMap`, the pure `ProjectionDefinition` unit, `ProjectionSnapshot`'s consistent cut, the change feed |
|
||||
| [session-telemetry.md](session-telemetry.md) | the outbound session-reporting capability seam: `SessionTelemetryRecord`/`SessionTelemetrySeverity`, the `SessionTelemetrySink` contract, and the `session-telemetry/record` redact waterfall |
|
||||
| [product-telemetry.md](product-telemetry.md) | Explicit product analytics submission and OTLP/HTTP transport |
|
||||
|
||||
> Type declarations and their JSDoc on these pages are source-equivalent and drift-checked by `pnpm run verify-type-equiv` (see [development.md](../development.md#documenting-types-verbatim-ts-type-equiv)). Ordinary blocks preserve complete declarations; `public-api` blocks preserve body-stripped public class declarations. Cordis services and events use each page's generated **Cordis API** section.
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
| 页面 | 负责内容 |
|
||||
|---|---|
|
||||
| [product-telemetry.md](product-telemetry.zh.md) | 显式产品分析事件提交与 OTLP/HTTP 传输 |
|
||||
| [boot.md](boot.zh.md) | 当前 profile 插件管理与 launcher 重载协调 |
|
||||
| [core.md](core.zh.md) | `packages/core` 如何控制 agent loop(智能体循环):逐包的循环说明、agent 创建与所有权(`AgentHandle`)、`Agent` 句柄的投递/取消/拦截约定,以及全仓通用类型模式(`…Map → derived-union`、品牌化 id) |
|
||||
| [llm-streaming.md](llm-streaming.zh.md) | `packages/llm` 的对话类型——`Message`/`ContentBlock`、组装完成的模型请求、`StreamChunk` wire protocol 和适配器约定(adapter contract)、`BlockAssembler`,以及 `LlmAdapter` 提供方约定 |
|
||||
@@ -68,5 +67,6 @@
|
||||
| [conversation.md](conversation.zh.md) | 目标无关的会话事件组装:上下文标识、位置数据、回放路径、视图构建器与目标自有的渲染节点 |
|
||||
| [session-projection.md](session-projection.zh.md) | 投影 seam:`SessionProjectionMap`、纯函数 `ProjectionDefinition` 单元、`ProjectionSnapshot` 的一致切面、变更馈送 |
|
||||
| [session-telemetry.md](session-telemetry.zh.md) | 对外会话上报能力 seam:`SessionTelemetryRecord`/`SessionTelemetrySeverity`、`SessionTelemetrySink` 约定和 `session-telemetry/record` 脱敏 waterfall(瀑布式事件) |
|
||||
| [product-telemetry.md](product-telemetry.zh.md) | 显式产品分析事件提交与 OTLP/HTTP 传输 |
|
||||
|
||||
> 这些页面上的类型声明及其 JSDoc 与源码等价,并由 `pnpm run verify-type-equiv` 检查漂移(见 [development.md](../development.zh.md#documenting-types-verbatim-ts-type-equiv))。普通块保留完整声明;`public-api` 块保留去除实现体的公开 class 声明。Cordis 服务与事件使用每页生成的 **Cordis API** 小节。
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/product-telemetry.md
|
||||
product-telemetry.md: b9abdfc3a781ce5dc6237c25f2f7e0b045776125
|
||||
product-telemetry.zh.md: de132fec7c7a2e394bb3ee7ab6e8d745c0912b66
|
||||
product-telemetry.md: d86540d5a2b75d84f11120b80b7d107788799238
|
||||
product-telemetry.zh.md: 52ad1193630fc67f87c9c710027fc1ec5a5db98a
|
||||
@@ -8,6 +8,31 @@ The [product telemetry plugin](../../packages/host/product-telemetry-otel/README
|
||||
|
||||
Enqueue is synchronous and does not acknowledge delivery. The SDK owns batching and retry; local diagnostics report export failures. See the package README for configuration, shutdown, and loss limits.
|
||||
|
||||
## Record types
|
||||
|
||||
```ts type-equiv
|
||||
/** Scalar values accepted by the collector's Arrow attributes map. */
|
||||
type ProductTelemetryScalar = string | number | boolean
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Explicitly selected analytics fields; object values may contain scalars only. */
|
||||
interface ProductTelemetryRecord {
|
||||
/** Product/DA-owned event name. */
|
||||
eventName: string
|
||||
/** Human-readable summary; never a prompt, response, credential, or file contents. */
|
||||
body: string
|
||||
/** Event occurrence time in Unix milliseconds. Observation time is assigned on enqueue. */
|
||||
timestamp: number
|
||||
/** OTel severity; omitted values use INFO. */
|
||||
severityNumber?: SeverityNumber
|
||||
/** Business fields selected by the caller; no automatic device or account identity. */
|
||||
attributes?: Record<string, ProductTelemetryScalar | Record<string, ProductTelemetryScalar>>
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/host/product-telemetry-otel/src/index.ts`](../../packages/host/product-telemetry-otel/src/index.ts)
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
|
||||
@@ -8,6 +8,31 @@
|
||||
|
||||
入队同步完成,不代表送达确认。SDK 负责批量发送与重试;发送失败会产生本地诊断。配置、退出和丢失限制见包 README。
|
||||
|
||||
## 记录类型
|
||||
|
||||
```ts type-equiv
|
||||
/** Scalar values accepted by the collector's Arrow attributes map. */
|
||||
type ProductTelemetryScalar = string | number | boolean
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Explicitly selected analytics fields; object values may contain scalars only. */
|
||||
interface ProductTelemetryRecord {
|
||||
/** Product/DA-owned event name. */
|
||||
eventName: string
|
||||
/** Human-readable summary; never a prompt, response, credential, or file contents. */
|
||||
body: string
|
||||
/** Event occurrence time in Unix milliseconds. Observation time is assigned on enqueue. */
|
||||
timestamp: number
|
||||
/** OTel severity; omitted values use INFO. */
|
||||
severityNumber?: SeverityNumber
|
||||
/** Business fields selected by the caller; no automatic device or account identity. */
|
||||
attributes?: Record<string, ProductTelemetryScalar | Record<string, ProductTelemetryScalar>>
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/host/product-telemetry-otel/src/index.ts`](../../packages/host/product-telemetry-otel/src/index.ts)
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/README.md
|
||||
README.md: 5f759989eda6588e707a0c80c3035d0ba51ec950
|
||||
README.zh.md: 8a2784f5a3f99c9842b10d98d89b6d40936380b2
|
||||
README.md: 0ce8e9202763a2e974300b83239f02c256a2953e
|
||||
README.zh.md: 8e0650d8f6b00112c4fdb4a4dbc11671a2b3f394
|
||||
+1
-1
@@ -77,7 +77,7 @@ Every package lives in exactly one group; new packages join existing groups, and
|
||||
| [`acp/`](acp/README.md) | Automation-only Agent Client Protocol server |
|
||||
| [`interaction/`](interaction/README.md) | Human-collaboration plane: approval/interaction seams, permission preset, commands, ask-user tool |
|
||||
| [`boot/`](boot/README.md) | Shared app-bin boot glue |
|
||||
| [`host/`](host/README.md) | Web-GUI host half: API gateway + HTTP route server |
|
||||
| [`host/`](host/README.md) | Web GUI host services, directory picking, application launch, plugin inventory, and product telemetry |
|
||||
| [`client/`](client/README.md) | Web-GUI browser half: shell, wire, object services, slots, `ui-*` plugins |
|
||||
| [`test-support/`](test-support/README.md) | Test infrastructure (testkits, replay, Loader smokes) |
|
||||
| [`runtime-diagnostics/`](runtime-diagnostics/README.md) | Runtime diagnostics: package-owned invariant checks and reports |
|
||||
|
||||
@@ -77,7 +77,7 @@ harness 由 `packages/` 下的 npm 包组装而成,按能力系列分组:会
|
||||
| [`acp/`](acp/README.zh.md) | 仅面向自动化的 ACP(Agent Client Protocol)服务器 |
|
||||
| [`interaction/`](interaction/README.zh.md) | 人机协作平面:批准/交互 seam、权限预设、命令、询问用户的工具 |
|
||||
| [`boot/`](boot/README.zh.md) | 共享的 app bin 启动粘合层 |
|
||||
| [`host/`](host/README.zh.md) | web GUI 宿主半侧:API 网关 + HTTP 路由服务器 |
|
||||
| [`host/`](host/README.zh.md) | Web GUI 宿主服务、目录选择、应用启动、插件清单和产品遥测 |
|
||||
| [`client/`](client/README.zh.md) | web GUI 浏览器半侧:shell、协议层、对象服务、slot、`ui-*` 插件 |
|
||||
| [`test-support/`](test-support/README.zh.md) | 测试基础设施(testkit、回放、Loader 冒烟测试) |
|
||||
| [`runtime-diagnostics/`](runtime-diagnostics/README.zh.md) | 运行时诊断:按包归属的运行时不变式检查与报告 |
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/host/README.md
|
||||
README.md: 4de7a5fdd00be392d19a58f4bc424fce53c5e112
|
||||
README.zh.md: 80b74e0647c810b900f671d7d1256ab0d46107a9
|
||||
README.md: f5e7832539404b8fb1fb856973f08d5dd5c87eb5
|
||||
README.zh.md: 402358f4f4097630ba8deda4551d6bc52ff01b11
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: "Package map for the web GUI host half: the HTTP and SPA servers, workspace-directory picking implementations, the open-in-app launch routes, and the plugin inventory projection."
|
||||
description: "Package map for the web GUI host half: the HTTP and SPA servers, workspace-directory picking implementations, the open-in-app launch routes, the plugin inventory projection, and explicit product event reporting."
|
||||
kind: "package-group"
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
||||
|
||||
## Summary
|
||||
|
||||
The `host/` group provides the web GUI's plain HTTP server, the SPA dist server that serves the built Web shell, the workspace-directory picking seam with its native, browse, and adaptive composition packages, the open-in-app application probe and launch routes, and the read-only plugin inventory projection. These packages are product packages; the browser transport lives in [`client/`](../client/README.md), and the composed application is [`apps/cli`](../../apps/cli/README.md) booting the [`dsh-base` bundle](../bundle/base/cordis.patch.yml) that serves the web app under `apps/web/`. The picker backends replace one another behind the shared seam.
|
||||
The `host/` group provides the web GUI's plain HTTP server, the SPA dist server that serves the built Web shell, the workspace-directory picking seam with its native, browse, and adaptive composition packages, the open-in-app application probe and launch routes, the read-only plugin inventory projection, and explicit product event reporting. These packages are product packages; the browser transport lives in [`client/`](../client/README.md), and the composed application is [`apps/cli`](../../apps/cli/README.md) booting the [`dsh-base` bundle](../bundle/base/cordis.patch.yml) that serves the web app under `apps/web/`. The picker backends replace one another behind the shared seam.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -46,7 +46,6 @@ Start with the subsystem references for the transport and the workspace records,
|
||||
- [HTTP server subsystem](../../docs/subsystems/web-server.md) — the webserver's routes, matching order, and config.
|
||||
- [Workspace subsystem](../../docs/subsystems/workspace.md) — the workspace records the directory picker feeds.
|
||||
- [Web config-tree boot and transport layering](../../.agents/notes/implemented/architecture/2026-07-24-web-config-tree-boot-and-transport-layering.md) — ownership of the Web transport layers.
|
||||
|
||||
- [Product telemetry](../../docs/subsystems/product-telemetry.md) — explicit analytics event submission.
|
||||
|
||||
<a id="dev-note"></a>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: "Web GUI Host 侧的包映射:HTTP 与 SPA 服务器、工作区目录选择实现、open-in-app 启动路由和插件清单投影。"
|
||||
description: "Web GUI Host 侧的包映射:HTTP 与 SPA 服务器、工作区目录选择实现、open-in-app 启动路由、插件清单投影和显式产品事件上报。"
|
||||
kind: "package-group"
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ kind: "package-group"
|
||||
|
||||
## 概述
|
||||
|
||||
`host/` 组提供 Web GUI 的普通 HTTP 服务器、服务已构建 Web 壳的 SPA dist 服务器、带原生/浏览/自适应组合包的工作区目录选择 seam、open-in-app 的应用探测与启动路由,以及只读的插件清单投影。这些包都是产品包;浏览器传输位于 [`client/`](../client/README.zh.md),组合应用是 [`apps/cli`](../../apps/cli/README.zh.md),它启动 [`dsh-base` 组合包](../bundle/base/cordis.patch.yml) 来提供 `apps/web/` 下的 Web 应用。选择器后端可在共享 seam 后互相替换。
|
||||
`host/` 组提供 Web GUI 的普通 HTTP 服务器、服务已构建 Web 壳的 SPA dist 服务器、带原生/浏览/自适应组合包的工作区目录选择 seam、open-in-app 的应用探测与启动路由,只读的插件清单投影以及显式产品事件上报。这些包都是产品包;浏览器传输位于 [`client/`](../client/README.zh.md),组合应用是 [`apps/cli`](../../apps/cli/README.zh.md),它启动 [`dsh-base` 组合包](../bundle/base/cordis.patch.yml) 来提供 `apps/web/` 下的 Web 应用。选择器后端可在共享 seam 后互相替换。
|
||||
|
||||
## 目录
|
||||
|
||||
@@ -46,7 +46,6 @@ kind: "package-group"
|
||||
- [HTTP 服务器子系统](../../docs/subsystems/web-server.zh.md)——webserver 的路由、匹配顺序与配置。
|
||||
- [工作区子系统](../../docs/subsystems/workspace.zh.md)——目录选择器所喂给的工作区记录。
|
||||
- [Web 配置树启动与传输分层](../../.agents/notes/implemented/architecture/2026-07-24-web-config-tree-boot-and-transport-layering.zh.md)——Web 传输各层的所有权。
|
||||
|
||||
- [产品埋点](../../docs/subsystems/product-telemetry.zh.md)——显式提交分析事件。
|
||||
|
||||
<a id="dev-note"></a>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/host/product-telemetry-otel/README.md
|
||||
README.md: 4ce0a8744b46391b8dbe3d7d6739f0e0e1d118c5
|
||||
README.zh.md: 34075efae80c2e5275fbb782c485bea1b2661745
|
||||
README.md: af1f2c415fc21db874abc7bb0d9e25c7149fcabf
|
||||
README.zh.md: ffdb682d9a8a0a4bf406f71322e062f14b666cf2
|
||||
@@ -3,7 +3,7 @@ description: "Configure explicit product usage events, OTLP/HTTP routing, batchi
|
||||
kind: "package-reference"
|
||||
---
|
||||
|
||||
# @deepseek-ai/dsh-product-telemetry-otel
|
||||
# @deepseek-ai/dsh-host-product-telemetry-otel
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
@@ -25,14 +25,14 @@ Send selected product usage events to an OTLP/HTTP collector. Events carry a nam
|
||||
<a id="use-this-package"></a>
|
||||
## Use this package
|
||||
|
||||
Mount the plugin in a Cordis composition with the application identity; override the collector endpoint when needed. The shipped profiles do not mount it.
|
||||
Mount the plugin in a Cordis composition with the application identity; override the collector endpoint when needed. The shipped profiles do not mount it. Set `DSH_APP_VERSION` to the running application release version in the launcher environment; the schema rejects an absent version.
|
||||
|
||||
```yaml
|
||||
- name: '@deepseek-ai/dsh-product-telemetry-otel'
|
||||
- name: '@deepseek-ai/dsh-host-product-telemetry-otel'
|
||||
config:
|
||||
endpoint: https://dsh-otel-collector.deepseeksvc.com/v1/logs
|
||||
serviceName: dsh-telemetry-test
|
||||
serviceVersion: test
|
||||
serviceName: deepseek-harness
|
||||
serviceVersion: !!js process.env.DSH_APP_VERSION
|
||||
compression: gzip
|
||||
scheduledDelayMillis: 30000
|
||||
```
|
||||
@@ -49,6 +49,10 @@ Mount the plugin in a Cordis composition with the application identity; override
|
||||
| `exportTimeoutMillis` | `20000` | Processor batch export deadline |
|
||||
| `shutdownTimeoutMillis` | `21000` | Outer wait for shutdown; expiry reports possible loss |
|
||||
|
||||
The default endpoint routes explicitly submitted events to the production product collector. Test and custom deployments must override it. Only `x-channel` and SDK protocol headers reach the collector; ambient OTel headers and client certificates are not inherited.
|
||||
|
||||
The 30-second interval batches product events; the exporter has a 15-second retry window inside the processor’s 20-second batch deadline. The outer 21-second wait bounds plugin disposal, including SDK `forceFlush()` work that the processor deadline does not cover. An unreachable collector can delay disposal for the full 21 seconds. A full 2,048-record queue requires four 512-record batches and may not drain before that deadline. Interactive compositions needing a shorter exit should override these budgets; neither configuration guarantees delivery.
|
||||
|
||||
Consumers inject `productTelemetry` and call `emit()` with explicitly selected analytics fields. Event names and field semantics belong to their product and analytics owners. The plugin reads no Session, account, credential, or device identifier. Callers must exclude prompts, responses, file contents, credentials, and other unapproved values.
|
||||
|
||||
The collector expects a string body and attributes containing strings, numbers, booleans, or objects of those scalars. Callers supply occurrence time in milliseconds; the plugin assigns observation time and defaults severity to INFO. Invalid transport configuration fails at activation. Export failures produce local warnings without making event submission wait for the network.
|
||||
@@ -61,7 +65,7 @@ The collector expects a string body and attributes containing strings, numbers,
|
||||
<details>
|
||||
<summary>Implementation internals — click to expand</summary>
|
||||
|
||||
A private OTel logger feeds `BatchLogRecordProcessor` and the HTTP JSON exporter. The SDK owns queueing, transient-error retries, and compression; plugin disposal drains pending records with a bounded wait. Export completion is observed separately because SDK shutdown can resolve after a rejected export. No global OTel provider is installed.
|
||||
A private OTel logger feeds `BatchLogRecordProcessor` and the SDK HTTP delegate with its JSON log serializer. The delegate receives explicit headers and an HTTP agent; only shared timeout and compression settings use SDK environment resolution. The direct `@opentelemetry/core` dependency matches `sdk-logs` at 2.9.0 so exporter result enums share one TypeScript identity. The SDK owns queueing, transient-error retries, and compression; plugin disposal drains pending records with a bounded wait. Export completion is observed separately because SDK shutdown can resolve after a rejected export. No global OTel provider is installed.
|
||||
|
||||
[`src/index.ts`](src/index.ts) owns configuration and submission. No runtime invariant companion is published: delivery has no independent local acknowledgement to compare with the SDK's queue.
|
||||
|
||||
@@ -93,7 +97,7 @@ None; event submission does not change model requests.
|
||||
|
||||
Delivery and collection remain limited to the following capabilities.
|
||||
|
||||
- No business events, renderer transport, or account/device identity are wired automatically.
|
||||
- Callers own event selection, renderer-to-host transport, and any permitted identity attributes.
|
||||
- The queue is memory-only; overflow, network failure, and process exit can lose events. There is no durable outbox or warehouse acknowledgement.
|
||||
- The SDK batches by record count, not encoded bytes. Callers must keep records within the collector's 4 MB limit and choose batch sizes appropriate to the receiver.
|
||||
- Caller-selected strings are not redacted automatically. This package does not decide product disclosure or consent policy.
|
||||
|
||||
@@ -3,7 +3,7 @@ description: "配置显式产品使用事件、OTLP/HTTP 路由、批量发送
|
||||
kind: "package-reference"
|
||||
---
|
||||
|
||||
# @deepseek-ai/dsh-product-telemetry-otel
|
||||
# @deepseek-ai/dsh-host-product-telemetry-otel
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
@@ -25,14 +25,14 @@ kind: "package-reference"
|
||||
<a id="use-this-package"></a>
|
||||
## 使用本包
|
||||
|
||||
在 Cordis 组合中挂载插件并提供应用标识;需要时可覆盖接收地址。内置 profile 不挂载本插件。
|
||||
在 Cordis 组合中挂载插件并提供应用标识;需要时可覆盖接收地址。内置 profile 不挂载本插件。启动器环境须将 `DSH_APP_VERSION` 设为运行中应用的发布版本;缺少版本时 schema 会拒绝配置。
|
||||
|
||||
```yaml
|
||||
- name: '@deepseek-ai/dsh-product-telemetry-otel'
|
||||
- name: '@deepseek-ai/dsh-host-product-telemetry-otel'
|
||||
config:
|
||||
endpoint: https://dsh-otel-collector.deepseeksvc.com/v1/logs
|
||||
serviceName: dsh-telemetry-test
|
||||
serviceVersion: test
|
||||
serviceName: deepseek-harness
|
||||
serviceVersion: !!js process.env.DSH_APP_VERSION
|
||||
compression: gzip
|
||||
scheduledDelayMillis: 30000
|
||||
```
|
||||
@@ -49,6 +49,10 @@ kind: "package-reference"
|
||||
| `exportTimeoutMillis` | `20000` | Processor 批次导出的超时时间 |
|
||||
| `shutdownTimeoutMillis` | `21000` | 退出时的等待上限;超时会提示可能丢失数据 |
|
||||
|
||||
默认接收地址将显式提交的事件发送到生产产品 collector,测试和自定义部署必须覆盖该地址。只向 collector 发送 `x-channel` 和 SDK 协议请求头,不继承宿主 OTel 请求头或客户端证书。
|
||||
|
||||
30 秒间隔用于批量发送产品事件;exporter 的 15 秒重试窗口位于 processor 的 20 秒批次期限内。外层 21 秒等待限制插件卸载时间,包括 processor 期限未覆盖的 SDK `forceFlush()`。collector 不可达时,卸载可能等待完整的 21 秒。2,048 条满队列需要四个 512 条批次,可能无法在期限前排空。要求更快退出的交互式应用组合应覆盖这些时间配置;两种配置都不保证送达。
|
||||
|
||||
消费方注入 `productTelemetry`,调用 `emit()` 提交明确选定的分析字段。事件名称与字段含义由产品和数据分析负责人定义。插件不读取 Session、账号、凭证或设备标识。调用方必须排除提示词、回答、文件内容、凭证及其他未经批准的数据。
|
||||
|
||||
接收服务要求 body 为字符串,attributes 的值为字符串、数字、布尔值或由这些标量组成的对象。调用方以毫秒提供发生时间;插件填写观测时间,默认严重程度为 INFO。无效传输配置在激活时报错。发送失败产生本地警告,事件提交不等待网络。
|
||||
@@ -61,7 +65,7 @@ kind: "package-reference"
|
||||
<details>
|
||||
<summary>实现细节——点击展开</summary>
|
||||
|
||||
私有 OTel logger 将记录交给 `BatchLogRecordProcessor` 和 HTTP JSON exporter。SDK 负责队列、临时错误重试与压缩;插件卸载时在限定时间内发送剩余记录。发送完成结果单独观测,因为 SDK 在发送被拒绝后仍可能正常完成退出。不安装全局 OTel provider。
|
||||
私有 OTel logger 将记录交给 `BatchLogRecordProcessor`,再由 SDK HTTP delegate 和 JSON 日志序列化器发送。delegate 使用显式请求头和 HTTP agent,只有共享的超时与压缩配置使用 SDK 环境变量解析。直接依赖的 `@opentelemetry/core` 与 `sdk-logs` 对齐为 2.9.0,使导出结果枚举共享同一个 TypeScript 类型身份。SDK 负责队列、临时错误重试与压缩;插件卸载时在限定时间内发送剩余记录。发送完成结果单独观测,因为 SDK 在发送被拒绝后仍可能正常完成退出。不安装全局 OTel provider。
|
||||
|
||||
[`src/index.ts`](src/index.ts) 负责配置与提交。不发布运行时不变量伴随模块:本地没有独立的送达确认可与 SDK 队列比较。
|
||||
|
||||
@@ -93,7 +97,7 @@ kind: "package-reference"
|
||||
|
||||
发送和采集受以下能力限制。
|
||||
|
||||
- 不自动接入业务事件、渲染进程传输或账号与设备标识。
|
||||
- 调用方拥有事件选择、渲染进程到宿主的传输以及获准使用的标识属性。
|
||||
- 队列仅存在于内存;队列溢出、网络故障和进程退出可能丢失事件。没有持久发件箱或入仓确认。
|
||||
- SDK 按记录条数而非编码字节数分批。调用方必须确保记录不超过接收服务的 4 MB 限制,并选择接收服务适用的批次大小。
|
||||
- 不自动脱敏调用方选择的字符串。本包不决定产品告知或同意策略。
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-product-telemetry-otel",
|
||||
"name": "@deepseek-ai/dsh-host-product-telemetry-otel",
|
||||
"description": "Explicit product usage events exported through OpenTelemetry HTTP logs",
|
||||
"version": "0.1.6-alpha.2",
|
||||
"publishConfig": {
|
||||
@@ -27,13 +27,14 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "^0.220.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.220.0",
|
||||
"@opentelemetry/otlp-exporter-base": "^0.220.0",
|
||||
"@opentelemetry/resources": "^2.9.0",
|
||||
"@opentelemetry/sdk-logs": "^0.220.0",
|
||||
"@deepseek-ai/schemastery": "workspace:^",
|
||||
"@opentelemetry/core": "2.9.0"
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/api-logs": "^0.220.0",
|
||||
"@opentelemetry/core": "2.9.0",
|
||||
"@opentelemetry/otlp-exporter-base": "^0.220.0",
|
||||
"@opentelemetry/otlp-transformer": "^0.220.0",
|
||||
"@opentelemetry/resources": "^2.9.0",
|
||||
"@opentelemetry/sdk-logs": "^0.220.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
import { Context, Service } from '@deepseek-ai/cordis'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import { SeverityNumber, type Logger } from '@opentelemetry/api-logs'
|
||||
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'
|
||||
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base'
|
||||
import { validateHeaderValue } from 'node:http'
|
||||
import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'
|
||||
import { createOtlpHttpExportDelegate, getSharedConfigurationFromEnvironment, httpAgentFactoryFromOptions } from '@opentelemetry/otlp-exporter-base/node-http'
|
||||
import { CompressionAlgorithm, getSharedConfigurationDefaults, mergeOtlpSharedConfigurationWithDefaults, OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'
|
||||
import { ExportResultCode } from '@opentelemetry/core'
|
||||
import { resourceFromAttributes } from '@opentelemetry/resources'
|
||||
import { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs'
|
||||
@@ -59,10 +61,10 @@ export interface Config {
|
||||
|
||||
const positiveInteger = () => z.number().step(1).min(1).max(2_147_483_647)
|
||||
|
||||
/** Loader validation; the same schema resolves defaults for direct construction. */
|
||||
/** Loader validation and defaults for application compositions. */
|
||||
export const Config: z<Partial<Config>, Config> = z.object({
|
||||
endpoint: z.string().default('https://dsh-otel-collector.deepseeksvc.com/v1/logs'),
|
||||
channel: z.string().default('dsh_otel_report'),
|
||||
channel: z.string().min(1).default('dsh_otel_report'),
|
||||
serviceName: z.string().required(),
|
||||
serviceVersion: z.string().required(),
|
||||
compression: z.union(['none', 'gzip']),
|
||||
@@ -79,9 +81,18 @@ export default class ProductTelemetry extends Service {
|
||||
static Config = Config
|
||||
private readonly logger: Logger
|
||||
|
||||
constructor(ctx: Context, input: Config) {
|
||||
const config = Config(input)
|
||||
const endpoint = new URL(config.endpoint)
|
||||
constructor(ctx: Context, config: Config) {
|
||||
let endpoint: URL
|
||||
try {
|
||||
endpoint = new URL(config.endpoint)
|
||||
} catch (cause) {
|
||||
throw new Error('product-telemetry-otel: endpoint must be a valid HTTP(S) URL', { cause })
|
||||
}
|
||||
try {
|
||||
validateHeaderValue('x-channel', config.channel)
|
||||
} catch (cause) {
|
||||
throw new Error('product-telemetry-otel: channel must be a valid HTTP header value', { cause })
|
||||
}
|
||||
if (!['http:', 'https:'].includes(endpoint.protocol)) {
|
||||
throw new Error('product-telemetry-otel: endpoint must use HTTP or HTTPS')
|
||||
}
|
||||
@@ -89,14 +100,20 @@ export default class ProductTelemetry extends Service {
|
||||
throw new Error('product-telemetry-otel: maxExportBatchSize must not exceed maxQueueSize')
|
||||
}
|
||||
super(ctx, 'productTelemetry')
|
||||
const exporter = new OTLPLogExporter({
|
||||
url: config.endpoint,
|
||||
headers: { 'x-channel': config.channel },
|
||||
const shared = mergeOtlpSharedConfigurationWithDefaults({
|
||||
timeoutMillis: config.timeoutMillis,
|
||||
...(config.compression === undefined ? {} : {
|
||||
compression: config.compression === 'gzip' ? CompressionAlgorithm.GZIP : CompressionAlgorithm.NONE,
|
||||
}),
|
||||
})
|
||||
}, getSharedConfigurationFromEnvironment('LOGS'), getSharedConfigurationDefaults())
|
||||
const transport = {
|
||||
...shared,
|
||||
url: config.endpoint,
|
||||
// This collector must not inherit another endpoint's headers or TLS client identity.
|
||||
headers: () => Promise.resolve({ 'Content-Type': 'application/json', 'x-channel': config.channel }),
|
||||
agentFactory: httpAgentFactoryFromOptions({ keepAlive: true }),
|
||||
}
|
||||
const exporter = new OTLPExporterBase(createOtlpHttpExportDelegate(transport, JsonLogsSerializer))
|
||||
const provider = new LoggerProvider({
|
||||
resource: resourceFromAttributes({
|
||||
'service.name': config.serviceName,
|
||||
@@ -111,7 +128,7 @@ export default class ProductTelemetry extends Service {
|
||||
export: (records, callback) => {
|
||||
exporter.export(records, (result) => {
|
||||
// SDK flush/shutdown can resolve after export failure; observe the actual completion.
|
||||
if (result.code !== ExportResultCode.SUCCESS) ctx.logger.warn('Product telemetry export failed')
|
||||
if (result.code !== ExportResultCode.SUCCESS) ctx.logger.warn('Product telemetry export failed', result.error)
|
||||
callback(result)
|
||||
})
|
||||
},
|
||||
@@ -120,7 +137,7 @@ export default class ProductTelemetry extends Service {
|
||||
},
|
||||
})],
|
||||
})
|
||||
this.logger = provider.getLogger('@deepseek-ai/dsh-product-telemetry-otel')
|
||||
this.logger = provider.getLogger('@deepseek-ai/dsh-host-product-telemetry-otel')
|
||||
ctx.effect(() => async () => {
|
||||
let timer!: ReturnType<typeof setTimeout>
|
||||
const deadline = new Promise<void>((resolve) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createServer } from 'node:http'
|
||||
import { once } from 'node:events'
|
||||
import { gunzipSync } from 'node:zlib'
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import type {} from '@deepseek-ai/dsh-product-telemetry-otel'
|
||||
import type {} from '@deepseek-ai/dsh-host-product-telemetry-otel'
|
||||
import { bootProductionProfile } from '../../../../test-support/loader-smoke/tests/fixtures/production-profile.ts'
|
||||
|
||||
const configPath = process.argv[2]
|
||||
@@ -24,6 +24,7 @@ try {
|
||||
await once(server, 'listening')
|
||||
const address = server.address()
|
||||
if (address === null || typeof address === 'string') throw new Error('collector has no port')
|
||||
process.env.DSH_APP_VERSION = 'synthetic-release'
|
||||
process.env.DSH_PRODUCT_TELEMETRY_TEST_ENDPOINT = `http://127.0.0.1:${address.port}/v1/logs`
|
||||
const ctx = await bootProductionProfile({ binName: 'product-telemetry-test', profile: 'headless', overlayPaths: [configPath] })
|
||||
try {
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
disabled: true
|
||||
- insert:
|
||||
- id: product-telemetry-otel
|
||||
name: '@deepseek-ai/dsh-product-telemetry-otel'
|
||||
name: '@deepseek-ai/dsh-host-product-telemetry-otel'
|
||||
config:
|
||||
endpoint: !!js process.env.DSH_PRODUCT_TELEMETRY_TEST_ENDPOINT
|
||||
serviceName: dsh-telemetry-test
|
||||
serviceVersion: test
|
||||
serviceVersion: !!js process.env.DSH_APP_VERSION
|
||||
compression: gzip
|
||||
scheduledDelayMillis: 60000
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createServer, type IncomingHttpHeaders } from 'node:http'
|
||||
import { once } from 'node:events'
|
||||
import { gunzipSync } from 'node:zlib'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { LoggerProvider } from '@opentelemetry/sdk-logs'
|
||||
import { SeverityNumber } from '@opentelemetry/api-logs'
|
||||
@@ -16,11 +16,17 @@ afterEach(async () => {
|
||||
try {
|
||||
for (const dispose of cleanup.splice(0).reverse()) await dispose()
|
||||
} finally {
|
||||
vi.unstubAllEnvs()
|
||||
vi.restoreAllMocks()
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubEnv('OTEL_EXPORTER_OTLP_COMPRESSION', undefined)
|
||||
vi.stubEnv('OTEL_EXPORTER_OTLP_LOGS_COMPRESSION', undefined)
|
||||
})
|
||||
|
||||
async function collector(statuses = [200]) {
|
||||
const captures: Capture[] = []
|
||||
const server = createServer((req, res) => {
|
||||
@@ -92,6 +98,22 @@ describe('explicit product telemetry', () => {
|
||||
expect(JSON.stringify(capture)).not.toContain('user.id')
|
||||
})
|
||||
|
||||
it('isolates collector headers from ambient OpenTelemetry credentials', async () => {
|
||||
vi.stubEnv('OTEL_EXPORTER_OTLP_HEADERS', 'Authorization=Bearer%20synthetic-secret,x-user-id=synthetic-user')
|
||||
vi.stubEnv('OTEL_EXPORTER_OTLP_LOGS_HEADERS', 'x-log-token=synthetic-token,x-channel=other-collector')
|
||||
const target = await collector()
|
||||
const ctx = context()
|
||||
const fiber = await ctx.plugin(ProductTelemetry, config(target.endpoint))
|
||||
ctx.productTelemetry.emit(event)
|
||||
await fiber.dispose()
|
||||
expect(target.captures).toHaveLength(1)
|
||||
expect(target.captures[0]?.headers).toMatchObject({ 'x-channel': 'dsh_otel_report' })
|
||||
expect(target.captures[0]?.headers).not.toHaveProperty('authorization')
|
||||
expect(target.captures[0]?.headers).not.toHaveProperty('x-user-id')
|
||||
expect(target.captures[0]?.headers).not.toHaveProperty('x-log-token')
|
||||
expect(process.env['OTEL_EXPORTER_OTLP_HEADERS']).toContain('synthetic-secret')
|
||||
})
|
||||
|
||||
it('does not export on mount or empty shutdown', async () => {
|
||||
const target = await collector()
|
||||
const fiber = await context().plugin(ProductTelemetry, config(target.endpoint))
|
||||
@@ -124,11 +146,12 @@ describe('explicit product telemetry', () => {
|
||||
expect(() => { ctx.productTelemetry.emit(event) }).not.toThrow()
|
||||
await fiber.dispose()
|
||||
expect(target.captures).toHaveLength(1)
|
||||
expect(warn).toHaveBeenCalledWith('Product telemetry export failed')
|
||||
expect(warn).toHaveBeenCalledWith('Product telemetry export failed', expect.any(Error))
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ endpoint: 'broken' }, { endpoint: 'ftp://collector.test/logs' },
|
||||
{ channel: '' }, { channel: 'bad\nchannel' }, { channel: '中文' }, { timeoutMillis: 0 },
|
||||
{ maxExportBatchSize: 0 }, { maxExportBatchSize: 2, maxQueueSize: 1 },
|
||||
{ maxQueueSize: -1 }, { scheduledDelayMillis: 0 }, { exportTimeoutMillis: Infinity },
|
||||
{ shutdownTimeoutMillis: 2_147_483_648 },
|
||||
@@ -138,6 +161,29 @@ describe('explicit product telemetry', () => {
|
||||
expect(ctx.get('productTelemetry')).toBeUndefined()
|
||||
})
|
||||
|
||||
it.each([
|
||||
[{ endpoint: 'broken' }, 'endpoint must be a valid HTTP(S) URL'],
|
||||
[{ channel: 'bad\nchannel' }, 'channel must be a valid HTTP header value'],
|
||||
] as const)('names invalid transport fields before registering the service', (invalid, message) => {
|
||||
const ctx = context()
|
||||
expect(() => new ProductTelemetry(ctx, config('http://collector.test/v1/logs', invalid)))
|
||||
.toThrow(`product-telemetry-otel: ${message}`)
|
||||
expect(ctx.get('productTelemetry')).toBeUndefined()
|
||||
})
|
||||
|
||||
it.each([
|
||||
['OTEL_EXPORTER_OTLP_COMPRESSION', 'gzip'],
|
||||
['OTEL_EXPORTER_OTLP_LOGS_COMPRESSION', 'gzip'],
|
||||
])('honors %s when compression is omitted', async (name, value) => {
|
||||
vi.stubEnv(name, value)
|
||||
const target = await collector()
|
||||
const ctx = context()
|
||||
const fiber = await ctx.plugin(ProductTelemetry, config(target.endpoint))
|
||||
ctx.productTelemetry.emit(event)
|
||||
await fiber.dispose()
|
||||
expect(target.captures[0]?.headers['content-encoding']).toBe('gzip')
|
||||
})
|
||||
|
||||
it('bounds a stalled shutdown and observes its later settlement', async () => {
|
||||
const target = await collector()
|
||||
const ctx = context()
|
||||
|
||||
Generated
+5
-2
@@ -8441,16 +8441,19 @@ importers:
|
||||
'@deepseek-ai/schemastery':
|
||||
specifier: link:../../../vendor/schemastery
|
||||
version: link:../../../vendor/schemastery
|
||||
'@opentelemetry/api':
|
||||
specifier: ^1.9.1
|
||||
version: 1.9.1
|
||||
'@opentelemetry/api-logs':
|
||||
specifier: ^0.220.0
|
||||
version: 0.220.0
|
||||
'@opentelemetry/core':
|
||||
specifier: 2.9.0
|
||||
version: 2.9.0(@opentelemetry/[email protected])
|
||||
'@opentelemetry/exporter-logs-otlp-http':
|
||||
'@opentelemetry/otlp-exporter-base':
|
||||
specifier: ^0.220.0
|
||||
version: 0.220.0(@opentelemetry/[email protected])
|
||||
'@opentelemetry/otlp-exporter-base':
|
||||
'@opentelemetry/otlp-transformer':
|
||||
specifier: ^0.220.0
|
||||
version: 0.220.0(@opentelemetry/[email protected])
|
||||
'@opentelemetry/resources':
|
||||
|
||||
@@ -31,7 +31,7 @@ interface ServiceRole {
|
||||
key: string
|
||||
pkg: string
|
||||
title: string
|
||||
mode: 'core' | 'seam' | 'bundle'
|
||||
mode: 'core' | 'seam' | 'bundle' | 'service'
|
||||
implementations?: string[]
|
||||
consumers?: string[]
|
||||
companions?: string[]
|
||||
@@ -99,13 +99,6 @@ const GROUP_ORDER = [
|
||||
]
|
||||
|
||||
const SERVICE_ROLES: ServiceRole[] = [
|
||||
{
|
||||
key: 'productTelemetry',
|
||||
pkg: 'product-telemetry-otel',
|
||||
title: 'Product usage event sender',
|
||||
mode: 'core',
|
||||
note: 'Exports explicitly submitted analytics events through OTLP/HTTP; mounting alone collects nothing.',
|
||||
},
|
||||
{
|
||||
key: 'hmr',
|
||||
pkg: 'hmr',
|
||||
@@ -380,6 +373,14 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
consumers: ['llm-pi-ai'],
|
||||
note: 'Flows are registered by the plugin that knows how to obtain one credential and keyed by the record they write; the seam owns the conversation and the one-attempt-per-key lifecycle, never the protocol.',
|
||||
},
|
||||
{
|
||||
key: 'productTelemetry',
|
||||
pkg: 'host-product-telemetry-otel',
|
||||
title: 'Product usage event sender',
|
||||
mode: 'service',
|
||||
note: 'Exports explicitly submitted analytics events through OTLP/HTTP; mounting alone collects nothing.',
|
||||
},
|
||||
|
||||
{
|
||||
key: 'sessionTelemetry',
|
||||
pkg: 'session-telemetry',
|
||||
@@ -870,7 +871,7 @@ function renderCapabilitySeams(pkgs: Pkg[], services: readonly ServiceEntry[]):
|
||||
const addEdge = (from: string, to: string): void => { edges.add(` ${from} --> ${to}`) }
|
||||
const lines = generatedHeader('Capability Seams And Core Services')
|
||||
lines.push(
|
||||
'A service can be a core spine service, a swappable capability seam, or a bundle/composition point. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.',
|
||||
'A service can be a core spine service, a swappable capability seam, a bundle/composition point, or a standalone service. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.',
|
||||
'',
|
||||
'```mermaid',
|
||||
'flowchart LR',
|
||||
|
||||
@@ -2256,6 +2256,16 @@
|
||||
"doc": "docs/subsystems/deliverables.md",
|
||||
"symbol": "WorkspaceChanges",
|
||||
"source": "packages/deliverables/workspace-changes/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/product-telemetry.md",
|
||||
"symbol": "ProductTelemetryScalar",
|
||||
"source": "packages/host/product-telemetry-otel/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/product-telemetry.md",
|
||||
"symbol": "ProductTelemetryRecord",
|
||||
"source": "packages/host/product-telemetry-otel/src/index.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -187,6 +187,7 @@
|
||||
"@deepseek-ai/dsh-host-directory-picker-native/*": ["./packages/host/directory-picker-native/src/*"],
|
||||
"@deepseek-ai/dsh-host-directory-picker-auto": ["./packages/host/directory-picker-auto/src"],
|
||||
"@deepseek-ai/dsh-host-directory-picker-auto/*": ["./packages/host/directory-picker-auto/src/*"],
|
||||
"@deepseek-ai/dsh-host-product-telemetry-otel": ["./packages/host/product-telemetry-otel/src"],
|
||||
"@deepseek-ai/dsh-host-open-in-app": ["./packages/host/open-in-app/src"],
|
||||
"@deepseek-ai/dsh-host-open-in-app/shared": ["./packages/host/open-in-app/src/shared.ts"],
|
||||
"@deepseek-ai/dsh-host-webserver": ["./packages/host/webserver/src"],
|
||||
@@ -402,7 +403,6 @@
|
||||
"@deepseek-ai/dsh-plan-mode": ["./packages/plan/plan-mode/src"],
|
||||
"@deepseek-ai/dsh-plan-mode/invariant": ["./packages/plan/plan-mode/src/invariant.ts"],
|
||||
"@deepseek-ai/dsh-plugin-package-inventory-deepseek": ["./packages/llm/plugin-package-inventory-deepseek/src"],
|
||||
"@deepseek-ai/dsh-product-telemetry-otel": ["./packages/host/product-telemetry-otel/src"],
|
||||
"@deepseek-ai/dsh-ptc-runtime": ["./packages/ptc-runtime/ptc-runtime/src"],
|
||||
"@deepseek-ai/dsh-ptc-runtime-node": ["./packages/ptc-runtime/ptc-runtime-node/src"],
|
||||
"@deepseek-ai/dsh-pwsh-sandbox": ["./packages/shell/pwsh-sandbox/src"],
|
||||
|
||||
Reference in new issue
Block a user