dsh-manage-session 2.0.3 → 2.0.4

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 (43) hide show
  1. package/CHANGELOG.ja.md +33 -33
  2. package/CHANGELOG.ko.md +33 -33
  3. package/CHANGELOG.md +33 -33
  4. package/INSTALL.ja.md +164 -164
  5. package/INSTALL.ko.md +164 -164
  6. package/INSTALL.md +164 -164
  7. package/INSTALL.zh.md +164 -164
  8. package/LICENSE +21 -21
  9. package/README.en.md +76 -62
  10. package/README.ja.md +53 -39
  11. package/README.ko.md +52 -38
  12. package/README.md +18 -4
  13. package/cordis.patch.yml +19 -19
  14. package/dsh.plugin.json +2 -2
  15. package/locale/de.json +6 -6
  16. package/locale/en.json +6 -6
  17. package/locale/es.json +6 -6
  18. package/locale/fr.json +6 -6
  19. package/locale/it.json +6 -6
  20. package/locale/ja.json +6 -6
  21. package/locale/ko.json +6 -6
  22. package/locale/ru.json +6 -6
  23. package/locale/zh.json +6 -6
  24. package/package.json +9 -9
  25. package/screenshots.json +3 -3
  26. package/src/client/bridge-scope.ts +202 -202
  27. package/src/client/card.tsx +154 -154
  28. package/src/client/host-api.ts +61 -61
  29. package/src/client/index.ts +594 -594
  30. package/src/client/locales.ts +893 -893
  31. package/src/client/manage-locales.ts +178 -178
  32. package/src/client/manage.tsx +430 -430
  33. package/src/client/panel.tsx +131 -131
  34. package/src/config.ts +43 -43
  35. package/src/host/health/attribution.ts +118 -118
  36. package/src/host/health/lossless.ts +128 -128
  37. package/src/host/health/source-kind.ts +350 -350
  38. package/src/host/history/archive-source.ts +250 -250
  39. package/src/host/history/archive.ts +249 -249
  40. package/src/host/history/favorites.ts +71 -71
  41. package/src/host/history/purge.ts +370 -370
  42. package/src/host/registry/archive-registry.ts +317 -317
  43. package/src/host/settings-bridge.ts +222 -222
@@ -1,128 +1,128 @@
1
- /**
2
- * 无损 JSON 判定 —— 本次事故的核心 gate 判据。
3
- *
4
- * 判定口径与宿主 `@deepseek-ai/dsh-util-values` 的 `isJsonValue` 一致:
5
- * 普通(或 null)原型的对象、可枚举的字符串键、有限数(含拒绝 -0)、非稀疏数组、
6
- * 无环;undefined / NaN / ±Infinity / 函数 / Symbol / BigInt 一律视为不可无损序列化。
7
- *
8
- * 通过宿主 `assertJsonArgs` / 投影缓存写入的数据都会先过这道口径,所以投影**状态**
9
- * 一旦含上述值,`api-session/added` 推送与检查点写入都会失败(症状错位的根因)。
10
- */
11
-
12
- /** 首个违规点位。 */
13
- export interface LosslessViolation {
14
- /** 到违规值的属性路径,例如 `.activeStep.lastSettled.actualTokens`。 */
15
- path: string
16
- /** 违规原因的可读描述。 */
17
- reason:
18
- | 'undefined'
19
- | 'NaN'
20
- | 'Infinity'
21
- | '-Infinity'
22
- | '-0'
23
- | 'function'
24
- | 'symbol'
25
- | 'bigint'
26
- | 'sparse-array-hole'
27
- | 'non-plain-prototype'
28
- | 'non-enumerable-or-symbol-key'
29
- | 'cycle'
30
- /** 违规处的实际类型(便于人工核对)。 */
31
- actual: string
32
- }
33
-
34
- /** 单个值是否可无损 JSON 序列化。 */
35
- export function isLossless(value: unknown): boolean {
36
- return firstLosslessViolation(value) === undefined
37
- }
38
-
39
- /** 判定一个普通对象是否「普通原型」(Object.prototype 或 null)。 */
40
- function isPlainObjectPrototype(value: object): boolean {
41
- const prototype = Object.getPrototypeOf(value) as object | null
42
- return prototype === null || prototype === Object.prototype
43
- }
44
-
45
- /**
46
- * 找出第一个破坏无损 JSON 的值;全部合规时返回 undefined。
47
- * @param value - 待判定的值(通常是投影状态)。
48
- * @param limit - 最多访问的节点数(防御超大对象)。
49
- * @returns 首个违规点位或 undefined。
50
- */
51
- export function firstLosslessViolation(value: unknown, limit = 200_000): LosslessViolation | undefined {
52
- const ancestors = new Set<object>()
53
- const stack: { node: unknown; path: string; leave?: boolean }[] = [{ node: value, path: '' }]
54
- let visited = 0
55
- while (stack.length > 0) {
56
- const task = stack.pop()
57
- if (task === undefined) break
58
- if (task.leave === true) {
59
- ancestors.delete(task.node as object)
60
- continue
61
- }
62
- if (visited >= limit) return { path: task.path, reason: 'cycle', actual: `node-limit(${limit})` }
63
- visited += 1
64
- const node = task.node
65
- if (node === undefined) return { path: task.path, reason: 'undefined', actual: 'undefined' }
66
- if (node === null) continue
67
- switch (typeof node) {
68
- case 'boolean':
69
- case 'string':
70
- continue
71
- case 'number':
72
- if (Number.isNaN(node)) return { path: task.path, reason: 'NaN', actual: 'NaN' }
73
- if (node === Infinity) return { path: task.path, reason: 'Infinity', actual: 'Infinity' }
74
- if (node === -Infinity) return { path: task.path, reason: '-Infinity', actual: '-Infinity' }
75
- if (Object.is(node, -0)) return { path: task.path, reason: '-0', actual: '-0' }
76
- continue
77
- case 'function':
78
- return { path: task.path, reason: 'function', actual: 'function' }
79
- case 'symbol':
80
- return { path: task.path, reason: 'symbol', actual: 'symbol' }
81
- case 'bigint':
82
- return { path: task.path, reason: 'bigint', actual: 'bigint' }
83
- default:
84
- break
85
- }
86
- const object = node as object
87
- if (ancestors.has(object)) return { path: task.path, reason: 'cycle', actual: 'object-cycle' }
88
- if (Array.isArray(object)) {
89
- if (!isPlainArray(object)) return { path: task.path, reason: 'non-plain-prototype', actual: 'array-subclass' }
90
- // 先按升序找出最低位的洞(报告口径确定),再倒序入栈以获得左→右的访问顺序。
91
- for (let index = 0; index < object.length; index += 1) {
92
- if (!Object.prototype.hasOwnProperty.call(object, index)) {
93
- return { path: `${task.path}[${index}]`, reason: 'sparse-array-hole', actual: 'hole' }
94
- }
95
- }
96
- ancestors.add(object)
97
- stack.push({ node: object, path: task.path, leave: true })
98
- for (let index = object.length - 1; index >= 0; index -= 1) {
99
- stack.push({ node: object[index], path: `${task.path}[${index}]` })
100
- }
101
- continue
102
- }
103
- if (!isPlainObjectPrototype(object)) {
104
- const name = (object as { constructor?: { name?: string } }).constructor?.name ?? 'unknown'
105
- return { path: task.path, reason: 'non-plain-prototype', actual: name }
106
- }
107
- const keys = Reflect.ownKeys(object)
108
- for (const key of keys) {
109
- if (typeof key !== 'string' || !Object.prototype.propertyIsEnumerable.call(object, key)) {
110
- return { path: `${task.path}.${String(key)}`, reason: 'non-enumerable-or-symbol-key', actual: typeof key }
111
- }
112
- }
113
- ancestors.add(object)
114
- stack.push({ node: object, path: task.path, leave: true })
115
- for (let index = keys.length - 1; index >= 0; index -= 1) {
116
- const key = keys[index]
117
- if (key === undefined || typeof key !== 'string') continue
118
- stack.push({ node: (object as Record<string, unknown>)[key], path: `${task.path}.${key}` })
119
- }
120
- }
121
- return undefined
122
- }
123
-
124
- /** 数组是否为普通数组(排除子类)。 */
125
- function isPlainArray(value: readonly unknown[]): boolean {
126
- const prototype = Object.getPrototypeOf(value) as object | null
127
- return prototype === null || prototype === Array.prototype
128
- }
1
+ /**
2
+ * 无损 JSON 判定 —— 本次事故的核心 gate 判据。
3
+ *
4
+ * 判定口径与宿主 `@deepseek-ai/dsh-util-values` 的 `isJsonValue` 一致:
5
+ * 普通(或 null)原型的对象、可枚举的字符串键、有限数(含拒绝 -0)、非稀疏数组、
6
+ * 无环;undefined / NaN / ±Infinity / 函数 / Symbol / BigInt 一律视为不可无损序列化。
7
+ *
8
+ * 通过宿主 `assertJsonArgs` / 投影缓存写入的数据都会先过这道口径,所以投影**状态**
9
+ * 一旦含上述值,`api-session/added` 推送与检查点写入都会失败(症状错位的根因)。
10
+ */
11
+
12
+ /** 首个违规点位。 */
13
+ export interface LosslessViolation {
14
+ /** 到违规值的属性路径,例如 `.activeStep.lastSettled.actualTokens`。 */
15
+ path: string
16
+ /** 违规原因的可读描述。 */
17
+ reason:
18
+ | 'undefined'
19
+ | 'NaN'
20
+ | 'Infinity'
21
+ | '-Infinity'
22
+ | '-0'
23
+ | 'function'
24
+ | 'symbol'
25
+ | 'bigint'
26
+ | 'sparse-array-hole'
27
+ | 'non-plain-prototype'
28
+ | 'non-enumerable-or-symbol-key'
29
+ | 'cycle'
30
+ /** 违规处的实际类型(便于人工核对)。 */
31
+ actual: string
32
+ }
33
+
34
+ /** 单个值是否可无损 JSON 序列化。 */
35
+ export function isLossless(value: unknown): boolean {
36
+ return firstLosslessViolation(value) === undefined
37
+ }
38
+
39
+ /** 判定一个普通对象是否「普通原型」(Object.prototype 或 null)。 */
40
+ function isPlainObjectPrototype(value: object): boolean {
41
+ const prototype = Object.getPrototypeOf(value) as object | null
42
+ return prototype === null || prototype === Object.prototype
43
+ }
44
+
45
+ /**
46
+ * 找出第一个破坏无损 JSON 的值;全部合规时返回 undefined。
47
+ * @param value - 待判定的值(通常是投影状态)。
48
+ * @param limit - 最多访问的节点数(防御超大对象)。
49
+ * @returns 首个违规点位或 undefined。
50
+ */
51
+ export function firstLosslessViolation(value: unknown, limit = 200_000): LosslessViolation | undefined {
52
+ const ancestors = new Set<object>()
53
+ const stack: { node: unknown; path: string; leave?: boolean }[] = [{ node: value, path: '' }]
54
+ let visited = 0
55
+ while (stack.length > 0) {
56
+ const task = stack.pop()
57
+ if (task === undefined) break
58
+ if (task.leave === true) {
59
+ ancestors.delete(task.node as object)
60
+ continue
61
+ }
62
+ if (visited >= limit) return { path: task.path, reason: 'cycle', actual: `node-limit(${limit})` }
63
+ visited += 1
64
+ const node = task.node
65
+ if (node === undefined) return { path: task.path, reason: 'undefined', actual: 'undefined' }
66
+ if (node === null) continue
67
+ switch (typeof node) {
68
+ case 'boolean':
69
+ case 'string':
70
+ continue
71
+ case 'number':
72
+ if (Number.isNaN(node)) return { path: task.path, reason: 'NaN', actual: 'NaN' }
73
+ if (node === Infinity) return { path: task.path, reason: 'Infinity', actual: 'Infinity' }
74
+ if (node === -Infinity) return { path: task.path, reason: '-Infinity', actual: '-Infinity' }
75
+ if (Object.is(node, -0)) return { path: task.path, reason: '-0', actual: '-0' }
76
+ continue
77
+ case 'function':
78
+ return { path: task.path, reason: 'function', actual: 'function' }
79
+ case 'symbol':
80
+ return { path: task.path, reason: 'symbol', actual: 'symbol' }
81
+ case 'bigint':
82
+ return { path: task.path, reason: 'bigint', actual: 'bigint' }
83
+ default:
84
+ break
85
+ }
86
+ const object = node as object
87
+ if (ancestors.has(object)) return { path: task.path, reason: 'cycle', actual: 'object-cycle' }
88
+ if (Array.isArray(object)) {
89
+ if (!isPlainArray(object)) return { path: task.path, reason: 'non-plain-prototype', actual: 'array-subclass' }
90
+ // 先按升序找出最低位的洞(报告口径确定),再倒序入栈以获得左→右的访问顺序。
91
+ for (let index = 0; index < object.length; index += 1) {
92
+ if (!Object.prototype.hasOwnProperty.call(object, index)) {
93
+ return { path: `${task.path}[${index}]`, reason: 'sparse-array-hole', actual: 'hole' }
94
+ }
95
+ }
96
+ ancestors.add(object)
97
+ stack.push({ node: object, path: task.path, leave: true })
98
+ for (let index = object.length - 1; index >= 0; index -= 1) {
99
+ stack.push({ node: object[index], path: `${task.path}[${index}]` })
100
+ }
101
+ continue
102
+ }
103
+ if (!isPlainObjectPrototype(object)) {
104
+ const name = (object as { constructor?: { name?: string } }).constructor?.name ?? 'unknown'
105
+ return { path: task.path, reason: 'non-plain-prototype', actual: name }
106
+ }
107
+ const keys = Reflect.ownKeys(object)
108
+ for (const key of keys) {
109
+ if (typeof key !== 'string' || !Object.prototype.propertyIsEnumerable.call(object, key)) {
110
+ return { path: `${task.path}.${String(key)}`, reason: 'non-enumerable-or-symbol-key', actual: typeof key }
111
+ }
112
+ }
113
+ ancestors.add(object)
114
+ stack.push({ node: object, path: task.path, leave: true })
115
+ for (let index = keys.length - 1; index >= 0; index -= 1) {
116
+ const key = keys[index]
117
+ if (key === undefined || typeof key !== 'string') continue
118
+ stack.push({ node: (object as Record<string, unknown>)[key], path: `${task.path}.${key}` })
119
+ }
120
+ }
121
+ return undefined
122
+ }
123
+
124
+ /** 数组是否为普通数组(排除子类)。 */
125
+ function isPlainArray(value: readonly unknown[]): boolean {
126
+ const prototype = Object.getPrototypeOf(value) as object | null
127
+ return prototype === null || prototype === Array.prototype
128
+ }