dsh-reference-session-collector 0.0.0-stage → 0.1.0

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.
package/package.json CHANGED
@@ -1,6 +1,92 @@
1
1
  {
2
2
  "name": "dsh-reference-session-collector",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "publishConfig": {
5
+ "registry": "https://registry.npmjs.org",
6
+ "tag": "latest",
7
+ "access": "public"
8
+ },
9
+ "description": "DSH Web 插件:会话原文投影同步器(DRS)——只读扫描 ~/.dsh/sessions 会话日志,解码后投影进自有 SQLite(segments/replaces/FTS5),经 /api/dsh-reference-session-collector 提供 surface 标记、召回对账与内容检索面。绝不写会话文件。",
10
+ "keywords": [
11
+ "deepseek-harness",
12
+ "dsh",
13
+ "dsh-plugin",
14
+ "fts5",
15
+ "projection",
16
+ "search",
17
+ "session",
18
+ "sqlite",
19
+ "tokenizer"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/drscrewdriver/dsh-reference-session-collector.git"
24
+ },
25
+ "type": "module",
26
+ "main": "lib/index.js",
27
+ "types": "lib/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./lib/index.d.ts",
31
+ "default": "./lib/index.js"
32
+ },
33
+ "./client": {
34
+ "default": "./lib/client.js"
35
+ },
36
+ "./cordis.patch.yml": "./cordis.patch.yml",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "dsh": {
40
+ "bundle": {
41
+ "patch": "./cordis.patch.yml"
42
+ },
43
+ "client": {
44
+ "platform": "web"
45
+ }
46
+ },
47
+ "scripts": {
48
+ "build": "node -e \"require('fs').rmSync('lib',{recursive:true,force:true})\" && tsdown",
49
+ "prepack": "npm run build",
50
+ "typecheck": "tsc -p tsconfig.json --noEmit --pretty false",
51
+ "smoke": "node scripts/smoke-lib.mjs",
52
+ "test": "vitest run",
53
+ "lint": "eslint src tests eslint.config.js",
54
+ "check": "npm run typecheck && npm run lint && npm run test && npm run build && npm run smoke"
55
+ },
56
+ "files": [
57
+ "README.md",
58
+ "README.en.md",
59
+ "README.ja.md",
60
+ "README.ko.md",
61
+ "README.de.md",
62
+ "README.es.md",
63
+ "README.fr.md",
64
+ "README.ru.md",
65
+ "README.it.md",
66
+ "cordis.patch.yml",
67
+ "dsh.plugin.json",
68
+ "lib",
69
+ "src",
70
+ "assets"
71
+ ],
72
+ "engines": {
73
+ "node": ">=22",
74
+ "dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2"
75
+ },
76
+ "license": "MIT",
77
+ "dependencies": {
78
+ "@huggingface/tokenizers": "0.1.3"
79
+ },
80
+ "devDependencies": {
81
+ "@deepseek-ai/cordis": "^4.0.1",
82
+ "@eslint/js": "^10.0.1",
83
+ "@types/node": "^24.0.0",
84
+ "eslint": "^10.10.0",
85
+ "globals": "^17.12.0",
86
+ "tsdown": "^0.22.2",
87
+ "tsx": "^4.23.12",
88
+ "typescript": "^5.6.0",
89
+ "typescript-eslint": "^8.69.0",
90
+ "vitest": "^4.1.1"
91
+ }
92
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 环境侧声明:DRS 不 value-import 任何宿主官方包(session-file-decode 的
3
+ * `@deepseek-ai/dsh-session` 只是运行时可选软加载,失败即回退本地解码器),
4
+ * 这里给动态 import 一个最小模块面让 tsc 在零宿主依赖下可解析。
5
+ */
6
+ declare module '@deepseek-ai/dsh-session' {
7
+ export function decodeStorageRecord(value: unknown): unknown[]
8
+ export function decodeSeqRanges(value: unknown, seq: number): number[]
9
+ }
@@ -0,0 +1,10 @@
1
+ // DRS v0 client 半身:合法**空插件**(T1.4 占位,装机实锤修正)。
2
+ // ⚠ 不能是裸 `export {}`:client 半身同样经 cordis 插件加载器装载
3
+ // (dsh-plugin-dev 双半契约),形态必须为 function 或 {apply}——空对象
4
+ // 触发宿主 "invalid plugin, expect function or object with an
5
+ // 'apply' method, received object"。
6
+ // 设置卡/面板随消费方接线相(二期)在 apply 里长槽位注入;本版 apply 为
7
+ // 合法 no-op。tsdown 横幅负责 __ModuleLoader__.load 包装与 id==包名契约。
8
+ export const inject: string[] = []
9
+
10
+ export function apply(): void {}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * ⚠ VENDORED(回搬件)——源自 dsh-context-compression-improved
3
+ * `packages/selector/src/deepseek-v4-tokenizer.ts`,回搬时点 2026-10-19。
4
+ * 回搬纪律:**文件级拷贝,禁止分叉**;CCI 侧修复须整文件重拷并复核资产哈希。
5
+ * 唯一授权 diff:导入路径 `./runtime/token-count.ts` → `./token-count.ts`
6
+ * (DRS 平铺布局,原目录树不存在),其余正文与源文件逐字节一致。
7
+ */
8
+ /** Offline DeepSeek tokenizers backed by pinned official Hugging Face assets. */
9
+
10
+ import { createHash } from 'node:crypto'
11
+ import { readFileSync } from 'node:fs'
12
+ import { Tokenizer } from '@huggingface/tokenizers'
13
+ import type { ExactTokenizerTokenCount } from './token-count.ts'
14
+
15
+ const TOKENIZER_ID = 'deepseek-ai/DeepSeek-V4-Pro'
16
+ const TOKENIZER_REVISION = '0e1a0e5e52aea73055f50fef6f2423db370265b6'
17
+ const TOKENIZER_SHA256 = '8f9f37ca37fdc4f5fd36d5cf4d3b0e8392edb4e894fd10cc0d70b4957c8633cf'
18
+ const CONFIG_SHA256 = '6ac8c8dc065ed118161d02dd532749ae3f52c243deac27872134fae2f50d8547'
19
+ const VISION_TOKENIZER_ID = 'deepseek-ai/DeepSeek-V4-Flash-Vision-Exp'
20
+ const VISION_TOKENIZER_REVISION = '6821d6ad3681a4b137b066b76094fa82ebd0a380'
21
+ const VISION_TOKENIZER_SHA256 = 'c90dfa01249db1be4245780a052ede752e1361c612ac6d08e2bdada7d599476b'
22
+ const VISION_CONFIG_SHA256 = '6ac8c8dc065ed118161d02dd532749ae3f52c243deac27872134fae2f50d8547'
23
+
24
+ /** Auditable origin and compatibility mapping for one bundled tokenizer. */
25
+ export interface DeepSeekTokenizerArtifactOrigin {
26
+ /** Hugging Face repository the assets were pinned from. */
27
+ readonly repository: string
28
+ /** Immutable commit revision the assets were pinned from. */
29
+ readonly revision: string
30
+ /** Upstream license of the repository. */
31
+ readonly license: string
32
+ /** Exact API wire model ids served by this artifact. */
33
+ readonly modelIds: readonly string[]
34
+ readonly tokenizerSha256: string
35
+ readonly tokenizerConfigSha256: string
36
+ }
37
+
38
+ /** Auditable origin and compatibility mapping for the bundled V4 Pro tokenizer. */
39
+ export const DEEPSEEK_V4_TOKENIZER_ARTIFACT: DeepSeekTokenizerArtifactOrigin = Object.freeze({
40
+ repository: TOKENIZER_ID,
41
+ revision: TOKENIZER_REVISION,
42
+ license: 'MIT',
43
+ tokenizerSha256: TOKENIZER_SHA256,
44
+ tokenizerConfigSha256: CONFIG_SHA256,
45
+ modelIds: Object.freeze(['deepseek-v4-flash', 'deepseek-v4-pro']),
46
+ })
47
+
48
+ /**
49
+ * Auditable origin and compatibility mapping for the bundled V4 Flash Vision
50
+ * tokenizer. The vision repository ships a distinct `tokenizer.json` (it adds
51
+ * the `<|deepseek_image|>` special token), so the vision model must never be
52
+ * mapped onto the V4 Pro tokenizer as an alias.
53
+ */
54
+ export const DEEPSEEK_VISION_TOKENIZER_ARTIFACT: DeepSeekTokenizerArtifactOrigin = Object.freeze({
55
+ repository: VISION_TOKENIZER_ID,
56
+ revision: VISION_TOKENIZER_REVISION,
57
+ license: 'MIT',
58
+ tokenizerSha256: VISION_TOKENIZER_SHA256,
59
+ tokenizerConfigSha256: VISION_CONFIG_SHA256,
60
+ modelIds: Object.freeze(['deepseek-v4-flash-vision-exp']),
61
+ })
62
+
63
+ /** Synchronous exact counter backed only by the verified local V4 artifacts. */
64
+ export interface DeepSeekV4TextTokenizer {
65
+ /** Count one fully determined text value with the official tokenizer. */
66
+ countText(text: string): ExactTokenizerTokenCount
67
+ }
68
+
69
+ /** Integrity tuple for one tokenizer JSON asset. @internal */
70
+ export interface DeepSeekV4TokenizerAssetDescriptor {
71
+ readonly bytes: number
72
+ readonly sha256: string
73
+ }
74
+
75
+ /** Injectable integrity manifest used by negative loader tests. @internal */
76
+ export interface DeepSeekV4TokenizerAssetIntegrity {
77
+ readonly tokenizer: DeepSeekV4TokenizerAssetDescriptor
78
+ readonly config: DeepSeekV4TokenizerAssetDescriptor
79
+ }
80
+
81
+ interface TokenizerArtifact {
82
+ readonly origin: DeepSeekTokenizerArtifactOrigin
83
+ readonly assetRoot: URL
84
+ readonly integrity: DeepSeekV4TokenizerAssetIntegrity
85
+ }
86
+
87
+ /**
88
+ * Every bundled artifact is registered with independent asset roots, integrity
89
+ * manifests, and cache entries: one corrupted artifact must never disable the
90
+ * tokenizer serving the other model family.
91
+ */
92
+ const ARTIFACTS: readonly TokenizerArtifact[] = Object.freeze([
93
+ Object.freeze({
94
+ origin: DEEPSEEK_V4_TOKENIZER_ARTIFACT,
95
+ assetRoot: new URL('../assets/deepseek-v4/', import.meta.url),
96
+ integrity: Object.freeze({
97
+ tokenizer: Object.freeze({ bytes: 6_367_146, sha256: TOKENIZER_SHA256 }),
98
+ config: Object.freeze({ bytes: 801, sha256: CONFIG_SHA256 }),
99
+ }),
100
+ }),
101
+ Object.freeze({
102
+ origin: DEEPSEEK_VISION_TOKENIZER_ARTIFACT,
103
+ assetRoot: new URL('../assets/deepseek-v4-vision-exp/', import.meta.url),
104
+ integrity: Object.freeze({
105
+ tokenizer: Object.freeze({ bytes: 6_367_257, sha256: VISION_TOKENIZER_SHA256 }),
106
+ config: Object.freeze({ bytes: 801, sha256: VISION_CONFIG_SHA256 }),
107
+ }),
108
+ }),
109
+ ])
110
+
111
+ interface ArtifactCacheEntry {
112
+ readonly tokenizer?: DeepSeekV4TextTokenizer
113
+ readonly failure?: string
114
+ }
115
+
116
+ const registryCache = new Map<DeepSeekTokenizerArtifactOrigin, ArtifactCacheEntry>()
117
+
118
+ function artifactForModel(modelId: string): TokenizerArtifact | undefined {
119
+ return ARTIFACTS.find(artifact => (artifact.origin.modelIds as readonly string[]).includes(modelId))
120
+ }
121
+
122
+ /**
123
+ * Resolve the shared offline tokenizer for one compatible API model.
124
+ * Unknown models and a cached asset/runtime failure return `undefined`; callers
125
+ * must report unavailable instead of manufacturing a character estimate.
126
+ * @param modelId - exact DeepSeek API wire model id.
127
+ * @returns the shared verified tokenizer, or undefined when unsupported/unavailable.
128
+ */
129
+ export function deepSeekV4TokenizerForModel(modelId: string): DeepSeekV4TextTokenizer | undefined {
130
+ const artifact = artifactForModel(modelId)
131
+ if (artifact === undefined) return undefined
132
+ const cached = registryCache.get(artifact.origin)
133
+ if (cached !== undefined) return cached.tokenizer
134
+ let entry: ArtifactCacheEntry
135
+ try {
136
+ const tokenizer = createDeepSeekV4TokenizerFromAssets(artifact.assetRoot, artifact.integrity, artifact.origin)
137
+ entry = { tokenizer }
138
+ } catch (error: unknown) {
139
+ entry = { failure: error instanceof Error ? error.message : String(error) }
140
+ }
141
+ registryCache.set(artifact.origin, entry)
142
+ return entry.tokenizer
143
+ }
144
+
145
+ /**
146
+ * Read the cached initialization diagnostic for provider registration.
147
+ * @param modelId - optional exact model id; defaults to the V4 Pro artifact.
148
+ * @returns the first initialization failure for that artifact, or undefined.
149
+ */
150
+ export function deepSeekV4TokenizerFailureReason(modelId?: string): string | undefined {
151
+ const artifact = modelId === undefined ? ARTIFACTS[0] : artifactForModel(modelId)
152
+ if (artifact === undefined) return undefined
153
+ return registryCache.get(artifact.origin)?.failure
154
+ }
155
+
156
+ /** Complete pinned artifact inventory, in stable registration order. */
157
+ export function deepSeekTokenizerArtifacts(): readonly DeepSeekTokenizerArtifactOrigin[] {
158
+ return ARTIFACTS.map(artifact => artifact.origin)
159
+ }
160
+
161
+ /**
162
+ * Build a tokenizer from one local asset directory after byte/hash validation.
163
+ * This provider-private seam exists so tests can prove every failure branch
164
+ * without mutating the committed artifact.
165
+ * @param assetRoot - local URL containing tokenizer.json and tokenizer_config.json.
166
+ * @param integrity - expected byte length and SHA-256 for both files.
167
+ * @param origin - auditable identity recorded on every returned count.
168
+ * @returns a synchronous exact text counter.
169
+ * @internal
170
+ */
171
+ export function createDeepSeekV4TokenizerFromAssets(
172
+ assetRoot: URL,
173
+ integrity: DeepSeekV4TokenizerAssetIntegrity,
174
+ origin: DeepSeekTokenizerArtifactOrigin = DEEPSEEK_V4_TOKENIZER_ARTIFACT,
175
+ ): DeepSeekV4TextTokenizer {
176
+ const tokenizerJson = readVerifiedJson(assetRoot, 'tokenizer.json', integrity.tokenizer)
177
+ const tokenizerConfig = readVerifiedJson(assetRoot, 'tokenizer_config.json', integrity.config)
178
+ const runtime = new Tokenizer(tokenizerJson, tokenizerConfig)
179
+ return Object.freeze({
180
+ countText(text: string): ExactTokenizerTokenCount {
181
+ const tokens = runtime.encode(text, { add_special_tokens: false }).ids.length
182
+ return Object.freeze({
183
+ kind: 'exact-tokenizer',
184
+ tokens,
185
+ tokenizerId: origin.repository,
186
+ tokenizerRevision: origin.revision,
187
+ })
188
+ },
189
+ })
190
+ }
191
+
192
+ function readVerifiedJson(
193
+ assetRoot: URL,
194
+ name: string,
195
+ descriptor: DeepSeekV4TokenizerAssetDescriptor,
196
+ ): Record<string, unknown> {
197
+ const bytes = readFileSync(new URL(name, assetRoot))
198
+ if (bytes.byteLength !== descriptor.bytes) {
199
+ throw new Error(
200
+ `DeepSeek tokenizer asset ${name} has ${String(bytes.byteLength)} bytes; expected ${String(descriptor.bytes)}`,
201
+ )
202
+ }
203
+ const actualSha256 = createHash('sha256').update(bytes).digest('hex')
204
+ if (actualSha256 !== descriptor.sha256) {
205
+ throw new Error(`DeepSeek tokenizer asset ${name} failed SHA-256 verification`)
206
+ }
207
+ const parsed: unknown = JSON.parse(bytes.toString('utf8'))
208
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
209
+ throw new Error(`DeepSeek tokenizer asset ${name} must contain a JSON object`)
210
+ }
211
+ return parsed as Record<string, unknown>
212
+ }