@jingyi0605/codingns4dsh 0.1.2 → 0.1.7-rc.3

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 (171) hide show
  1. package/README.en.md +4 -4
  2. package/README.md +14 -5
  3. package/data/build/dist/client/account-bar.js +57 -3
  4. package/data/build/dist/client/bundle.js +36246 -268
  5. package/data/build/dist/client/cli-slots.js +60 -18
  6. package/data/build/dist/client/dsh-h5-bootstrap.d.ts +2 -0
  7. package/data/build/dist/client/dsh-h5-bootstrap.js +79 -13
  8. package/data/build/dist/client/features/file-management-panel.d.ts +5 -0
  9. package/data/build/dist/client/features/file-management-panel.js +48 -0
  10. package/data/build/dist/client/features/file-management.d.ts +4 -0
  11. package/data/build/dist/client/features/file-management.js +50 -0
  12. package/data/build/dist/client/features/index.d.ts +10 -1
  13. package/data/build/dist/client/features/index.js +12 -1
  14. package/data/build/dist/client/features/login-protection-panel.js +1 -1
  15. package/data/build/dist/client/features/login-protection-session.d.ts +2 -0
  16. package/data/build/dist/client/features/login-protection-session.js +18 -0
  17. package/data/build/dist/client/features/peer-host.d.ts +4 -0
  18. package/data/build/dist/client/features/peer-host.js +89 -0
  19. package/data/build/dist/client/features/reverse-proxy-panel.js +23 -7
  20. package/data/build/dist/client/features/reverse-proxy.d.ts +1 -2
  21. package/data/build/dist/client/features/reverse-proxy.js +75 -5
  22. package/data/build/dist/client/features/types.d.ts +9 -0
  23. package/data/build/dist/client/features/workspace-session-enhancement-panel.js +10 -0
  24. package/data/build/dist/client/features/workspace-session-enhancement.js +18 -1
  25. package/data/build/dist/client/file-management-dom.d.ts +12 -0
  26. package/data/build/dist/client/file-management-dom.js +527 -0
  27. package/data/build/dist/client/git-management.js +24 -5
  28. package/data/build/dist/client/host-navigation.d.ts +29 -0
  29. package/data/build/dist/client/host-navigation.js +29 -0
  30. package/data/build/dist/client/host-router.d.ts +3 -0
  31. package/data/build/dist/client/host-router.js +2 -0
  32. package/data/build/dist/client/index.d.ts +6 -1
  33. package/data/build/dist/client/index.js +30 -10
  34. package/data/build/dist/client/locale.js +36 -7
  35. package/data/build/dist/client/peer-host-connection-button.d.ts +13 -0
  36. package/data/build/dist/client/peer-host-connection-button.js +69 -0
  37. package/data/build/dist/client/peer-host-management-api.d.ts +40 -0
  38. package/data/build/dist/client/peer-host-management-api.js +33 -0
  39. package/data/build/dist/client/peer-host-management-panel.d.ts +13 -0
  40. package/data/build/dist/client/peer-host-management-panel.js +181 -0
  41. package/data/build/dist/client/peer-host-native-session-ui.d.ts +60 -0
  42. package/data/build/dist/client/peer-host-native-session-ui.js +372 -0
  43. package/data/build/dist/client/peer-host-scoped-client.d.ts +67 -0
  44. package/data/build/dist/client/peer-host-scoped-client.js +316 -0
  45. package/data/build/dist/client/peer-host-session-controller.d.ts +30 -0
  46. package/data/build/dist/client/peer-host-session-controller.js +95 -0
  47. package/data/build/dist/client/quick-phrase-slot.js +14 -0
  48. package/data/build/dist/client/remote-web-context.js +26 -4
  49. package/data/build/dist/client/session-changed-files-view.d.ts +15 -0
  50. package/data/build/dist/client/session-changed-files-view.js +337 -0
  51. package/data/build/dist/client/settings-bridge.d.ts +14 -4
  52. package/data/build/dist/client/settings-bridge.js +59 -12
  53. package/data/build/dist/client/settings-section.js +17 -4
  54. package/data/build/dist/client/subscription-slot.js +21 -8
  55. package/data/build/dist/client/workspace-session-rightbar-dom.d.ts +28 -0
  56. package/data/build/dist/client/workspace-session-rightbar-dom.js +291 -0
  57. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +25 -33
  58. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +7 -1
  59. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +34 -11
  60. package/data/build/dist/dsh-capabilities/matrix.js +9 -0
  61. package/data/build/dist/dsh-capabilities/routes.js +35 -0
  62. package/data/build/dist/dsh-capabilities/settings-store.d.ts +12 -0
  63. package/data/build/dist/dsh-capabilities/settings-store.js +34 -0
  64. package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
  65. package/data/build/dist/features/host-router.d.ts +26 -0
  66. package/data/build/dist/features/host-router.js +110 -0
  67. package/data/build/dist/features/registry.js +3 -0
  68. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +13 -1
  69. package/data/build/dist/host/cli-adapters/codex-driver.js +528 -72
  70. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +21 -2
  71. package/data/build/dist/host/cli-adapters/command-code-driver.js +408 -105
  72. package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
  73. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +2 -0
  74. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +47 -28
  75. package/data/build/dist/host/cli-adapters/dsh-tool-history.js +37 -0
  76. package/data/build/dist/host/cli-adapters/feature.js +94 -10
  77. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +6 -0
  78. package/data/build/dist/host/cli-adapters/json-rpc-process.js +14 -3
  79. package/data/build/dist/host/cli-adapters/kimi-driver.js +3 -3
  80. package/data/build/dist/host/cli-adapters/model-catalog.d.ts +6 -0
  81. package/data/build/dist/host/cli-adapters/model-catalog.js +8 -0
  82. package/data/build/dist/host/cli-adapters/opencode-driver.js +14 -2
  83. package/data/build/dist/host/cli-adapters/pi-driver.js +2 -2
  84. package/data/build/dist/host/cli-adapters/process-utils.d.ts +8 -0
  85. package/data/build/dist/host/cli-adapters/process-utils.js +48 -0
  86. package/data/build/dist/host/cli-adapters/registry.d.ts +6 -1
  87. package/data/build/dist/host/cli-adapters/registry.js +41 -7
  88. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +23 -4
  89. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
  90. package/data/build/dist/host/cli-adapters/standard-stream-driver.js +36 -15
  91. package/data/build/dist/host/dsh-device-runtime.d.ts +2 -0
  92. package/data/build/dist/host/dsh-device-runtime.js +55 -29
  93. package/data/build/dist/host/dsh-runtime-version.d.ts +3 -2
  94. package/data/build/dist/host/dsh-runtime-version.js +71 -2
  95. package/data/build/dist/host/features/auth.js +10 -2
  96. package/data/build/dist/host/features/file-management.d.ts +5 -0
  97. package/data/build/dist/host/features/file-management.js +267 -0
  98. package/data/build/dist/host/features/git-management.js +24 -2
  99. package/data/build/dist/host/features/index.d.ts +2 -0
  100. package/data/build/dist/host/features/index.js +6 -0
  101. package/data/build/dist/host/features/peer-host.d.ts +22 -0
  102. package/data/build/dist/host/features/peer-host.js +322 -0
  103. package/data/build/dist/host/features/types.d.ts +4 -0
  104. package/data/build/dist/host/host-router.d.ts +3 -0
  105. package/data/build/dist/host/host-router.js +2 -0
  106. package/data/build/dist/host/index-injection.d.ts +17 -0
  107. package/data/build/dist/host/index-injection.js +24 -0
  108. package/data/build/dist/host/index.d.ts +4 -1
  109. package/data/build/dist/host/index.js +41 -7
  110. package/data/build/dist/host/lan-access-dsh.d.ts +34 -1
  111. package/data/build/dist/host/lan-access-dsh.js +145 -26
  112. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.d.ts +52 -0
  113. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.js +166 -0
  114. package/data/build/dist/host/modules/peer-host/host-ws-connector.d.ts +31 -0
  115. package/data/build/dist/host/modules/peer-host/host-ws-connector.js +108 -0
  116. package/data/build/dist/host/modules/peer-host/host-ws-proxy-service.d.ts +26 -0
  117. package/data/build/dist/host/modules/peer-host/host-ws-proxy-service.js +159 -0
  118. package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.d.ts +50 -0
  119. package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.js +97 -0
  120. package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.d.ts +19 -0
  121. package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.js +54 -0
  122. package/data/build/dist/host/modules/peer-host/peer-host-handshake.d.ts +44 -0
  123. package/data/build/dist/host/modules/peer-host/peer-host-handshake.js +133 -0
  124. package/data/build/dist/host/modules/peer-host/peer-host-relay.d.ts +65 -0
  125. package/data/build/dist/host/modules/peer-host/peer-host-relay.js +196 -0
  126. package/data/build/dist/host/modules/peer-host/peer-host-session.d.ts +42 -0
  127. package/data/build/dist/host/modules/peer-host/peer-host-session.js +152 -0
  128. package/data/build/dist/host/modules/peer-host/peer-host-store.d.ts +92 -0
  129. package/data/build/dist/host/modules/peer-host/peer-host-store.js +323 -0
  130. package/data/build/dist/host/modules/peer-host/peer-host-ws-gateway.d.ts +34 -0
  131. package/data/build/dist/host/modules/peer-host/peer-host-ws-gateway.js +156 -0
  132. package/data/build/dist/host/native-session-bridge.d.ts +18 -2
  133. package/data/build/dist/host/native-session-bridge.js +236 -8
  134. package/data/build/dist/host/remote-web-runtime.js +26 -1
  135. package/data/build/dist/host/rpc.js +8 -3
  136. package/data/build/dist/host/session-changes.d.ts +5 -0
  137. package/data/build/dist/host/session-changes.js +163 -0
  138. package/data/build/dist/host/settings.js +6 -0
  139. package/data/build/dist/index.d.ts +1 -1
  140. package/data/build/dist/index.js +1 -1
  141. package/data/build/dist/shared/contracts/cli-adapter.d.ts +23 -2
  142. package/data/build/dist/shared/contracts/config.d.ts +15 -0
  143. package/data/build/dist/shared/contracts/config.js +10 -0
  144. package/data/build/dist/shared/contracts/dsh-device.d.ts +2 -0
  145. package/data/build/dist/shared/contracts/errors.d.ts +14 -0
  146. package/data/build/dist/shared/contracts/errors.js +15 -0
  147. package/data/build/dist/shared/contracts/feature.d.ts +2 -0
  148. package/data/build/dist/shared/contracts/file-management.d.ts +6 -0
  149. package/data/build/dist/shared/contracts/file-management.js +3 -0
  150. package/data/build/dist/shared/contracts/peer-host.d.ts +116 -15
  151. package/data/build/dist/shared/contracts/peer-host.js +19 -0
  152. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  153. package/data/build/dist/shared/contracts/version.js +1 -1
  154. package/data/build/dist/shared/index.d.ts +4 -3
  155. package/data/build/dist/shared/index.js +2 -2
  156. package/data/build/dist/transport/debug.d.ts +5 -0
  157. package/data/build/dist/transport/debug.js +23 -1
  158. package/data/build/dist/transport/dsh-transport.d.ts +2 -0
  159. package/data/build/dist/transport/dsh-transport.js +11 -0
  160. package/data/build/dist/transport/webrtc-client.d.ts +2 -0
  161. package/data/build/dist/transport/webrtc-client.js +2 -1
  162. package/data/build/dist/transport/webrtc-host.d.ts +2 -0
  163. package/data/build/dist/transport/webrtc-host.js +6 -3
  164. package/data/build/dist/typert.host.d.ts +1 -1
  165. package/data/build/dist/typert.host.js +2 -1
  166. package/dsh.bundle.patch +10 -0
  167. package/package.json +19 -11
  168. package/profile/README.md +2 -2
  169. package/profile/package.json +2 -2
  170. package/profile/version.json +1 -1
  171. package/version.json +1 -1
@@ -16,9 +16,17 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
16
16
  ? ctx.on.bind(ctx)
17
17
  : undefined;
18
18
  const externalHandles = new Map();
19
+ const compactionStates = new Map();
20
+ const confirmedContextWindows = new Map();
21
+ // Agent.inject() 只是把消息放进队列,新的 DSH step 会在稍后的事件循环中
22
+ // 才创建。此期间 activeStep 仍然指向旧 step,工具追加必须暂缓,否则会把
23
+ // 下一步的工具写进旧 step;step/start 到达后由工具投影器重试。
24
+ const pendingStepTransitions = new Set();
19
25
  let injectedStepSequence = 0;
20
26
  const modernInjectedSource = isModernDshVersion(dshVersion);
21
27
  const appendNativeToolCall = (sessionId, call) => {
28
+ if (on !== undefined && pendingStepTransitions.has(sessionId))
29
+ return null;
22
30
  const session = appendableSession(store?.get(sessionId));
23
31
  const position = session === null ? null : activeStep(session);
24
32
  if (session === null || position === null)
@@ -91,11 +99,60 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
91
99
  // 投影先清空旧 pressure,再等待下一条 usage,ContextMeter 因而闪烁。
92
100
  // 保留已有容量并跳过等价事件,让用量更新直接落到同一个组件上。
93
101
  const previous = latestRequestContext(session);
94
- if (previous !== null
95
- && previous.provider === context.provider
96
- && previous.model === context.model
97
- && (context.contextWindow === undefined || previous.contextWindow === context.contextWindow))
98
- return true;
102
+ const contextKey = `${sessionId}\u0000${context.provider}\u0000${context.model}`;
103
+ if (previous !== null && previous.provider === context.provider && previous.model === context.model) {
104
+ const historicalWindow = historicalUsageContextWindow(session);
105
+ if (context.contextWindow === undefined) {
106
+ if (historicalWindow !== undefined && previous.contextWindow !== historicalWindow) {
107
+ session.append('request/context', {
108
+ provider: context.provider,
109
+ model: context.model,
110
+ contextWindow: historicalWindow,
111
+ });
112
+ }
113
+ return true;
114
+ }
115
+ if (previous.contextWindow === context.contextWindow
116
+ && context.confirmed === true
117
+ && historicalWindow !== undefined
118
+ && historicalWindow !== context.contextWindow) {
119
+ session.append('request/context', {
120
+ provider: context.provider,
121
+ model: context.model,
122
+ contextWindow: historicalWindow,
123
+ });
124
+ confirmedContextWindows.set(contextKey, historicalWindow);
125
+ return true;
126
+ }
127
+ if (previous.contextWindow === context.contextWindow) {
128
+ if (context.confirmed === true && context.source !== 'catalog' && context.contextWindow !== undefined) {
129
+ confirmedContextWindows.set(contextKey, context.contextWindow);
130
+ }
131
+ return true;
132
+ }
133
+ // 已经确认过的同一路由容量是会话级事实。迟到/全局 usage 不能覆盖它。
134
+ // DSH 每个新 step 会先写入一次通用 1M 占位值。catalog 提示必须能把
135
+ // 这个占位值恢复为已知容量,但真实 Provider usage 仍由锁定值保护。
136
+ if (confirmedContextWindows.has(contextKey) && context.source !== 'catalog')
137
+ return true;
138
+ // 进程重启后仍可从历史 usage 恢复稳定容量,避免尾部遗留的错误
139
+ // request/context=1M 在第二轮开始时再次成为当前窗口。
140
+ if (context.confirmed === true && historicalWindow !== undefined && historicalWindow !== context.contextWindow) {
141
+ if (previous.contextWindow !== historicalWindow) {
142
+ session.append('request/context', {
143
+ provider: context.provider,
144
+ model: context.model,
145
+ contextWindow: historicalWindow,
146
+ });
147
+ }
148
+ confirmedContextWindows.set(contextKey, historicalWindow);
149
+ return true;
150
+ }
151
+ // 允许本次运行首次明确确认的 usage 修正历史遗留的路由占位值,
152
+ // 例如旧日志中的 1M 随后被 Codex usage 明确纠正为 258400。
153
+ if (context.confirmed !== true)
154
+ return true;
155
+ }
99
156
  // Registry 在一轮开始时只能提供适配器身份,不能提供 Provider 容量。
100
157
  // 继承上一条已知容量,避免先写无容量事件导致 ContextMeter 卸载;
101
158
  // 真正的 usage 到达后,投影器会用 Provider 的最新容量覆盖它。
@@ -105,6 +162,9 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
105
162
  model: context.model,
106
163
  ...(contextWindow === undefined ? {} : { contextWindow }),
107
164
  });
165
+ if (context.confirmed === true && context.source !== 'catalog' && context.contextWindow !== undefined) {
166
+ confirmedContextWindows.set(contextKey, context.contextWindow);
167
+ }
108
168
  return true;
109
169
  }
110
170
  catch {
@@ -117,6 +177,18 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
117
177
  if (session === null || position === null)
118
178
  return false;
119
179
  try {
180
+ const previousContext = latestRequestContext(session);
181
+ const normalizedUsage = previousContext?.contextWindow !== undefined
182
+ && usage.contextWindow !== undefined
183
+ && usage.contextWindow !== previousContext.contextWindow
184
+ ? {
185
+ ...usage,
186
+ contextWindow: previousContext.contextWindow,
187
+ ...(usage.contextTokens === undefined ? {} : {
188
+ contextUsageRatio: Number(Math.min(1, usage.contextTokens / previousContext.contextWindow).toFixed(6)),
189
+ }),
190
+ }
191
+ : usage;
120
192
  session.append('assistant/attempt', {
121
193
  turn: position.turn,
122
194
  step: position.step,
@@ -125,7 +197,7 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
125
197
  time: Date.now(),
126
198
  chunk: {
127
199
  type: 'usage',
128
- usage: compactUsage(usage),
200
+ usage: compactUsage(normalizedUsage),
129
201
  },
130
202
  }],
131
203
  });
@@ -135,6 +207,76 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
135
207
  return false;
136
208
  }
137
209
  };
210
+ const appendNativeCompactionEvent = (sessionId, event) => {
211
+ const session = appendableSession(store?.get(sessionId));
212
+ if (session === null)
213
+ return false;
214
+ try {
215
+ if (event.phase === 'start') {
216
+ if (compactionStates.has(sessionId))
217
+ return true;
218
+ const compactionId = event.compactionId?.trim() || `codingns-compaction-${randomUUID()}`;
219
+ const position = activeStep(session);
220
+ session.append('compaction/start', {
221
+ compactionId,
222
+ turn: position?.turn ?? null,
223
+ });
224
+ compactionStates.set(sessionId, {
225
+ compactionId,
226
+ turn: position?.turn ?? null,
227
+ summaryAppended: false,
228
+ });
229
+ return true;
230
+ }
231
+ const state = compactionStates.get(sessionId);
232
+ if (state === undefined)
233
+ return false;
234
+ if ((event.phase === 'summary' || event.phase === 'end' && event.summary !== undefined) && !state.summaryAppended) {
235
+ const range = compactionSurfaceRange(session);
236
+ if (range !== null) {
237
+ const summary = boundCompactionSummary(event.summary);
238
+ session.append('compaction/summary', {
239
+ compactionId: state.compactionId,
240
+ summary: [{ type: 'text', text: summary }],
241
+ shadowedRange: { start: range.start, end: range.end },
242
+ shadowedSeqs: range.seqs,
243
+ shadowedTokenCount: safeCount(event.shadowedTokenCount),
244
+ provider: event.provider?.trim() || 'codingns-external',
245
+ model: event.model?.trim() || 'external-agent',
246
+ });
247
+ // DSH 前端的 compaction 节点由紧随 summary 的 checkpoint 替换事件生成。
248
+ // DSH 0.1.7 起会话格式只接受生产者自持的 source.kind,旧的 plugin 包装
249
+ // 会让整轮持久化失败;0.1.6 及更早仍由 dsh-compaction 的 plugin 标记识别。
250
+ session.append('user/message', {
251
+ id: `codingns-compaction-${state.compactionId}`,
252
+ role: 'user',
253
+ content: [{ type: 'text', text: summary }],
254
+ source: modernInjectedSource
255
+ ? { kind: 'compact-checkpoint', compactionId: state.compactionId }
256
+ : { kind: 'plugin', plugin: 'compact' },
257
+ }, {
258
+ surfaceOp: { op: 'replace', startSeq: range.start, endSeq: range.end },
259
+ sourceEventSeqs: range.seqs,
260
+ });
261
+ state.summaryAppended = true;
262
+ }
263
+ return true;
264
+ }
265
+ if (event.phase === 'end') {
266
+ session.append('compaction/end', {
267
+ compactionId: state.compactionId,
268
+ turn: state.turn,
269
+ ...(event.error?.trim() ? { error: event.error.trim().slice(0, 512) } : !state.summaryAppended ? { error: 'Provider 未返回可投影的压缩摘要。' } : {}),
270
+ });
271
+ compactionStates.delete(sessionId);
272
+ }
273
+ return true;
274
+ }
275
+ catch {
276
+ // 原生服务缺失或版本不支持 compaction 事件时,不能阻断 Provider 回合。
277
+ return false;
278
+ }
279
+ };
138
280
  const injectNativeNextStep = (sessionId, summary = '外部工具已完成,继续处理当前任务。') => {
139
281
  const agent = nativeAgent(ctx, sessionId);
140
282
  if (agent === null || typeof agent.inject !== 'function')
@@ -142,7 +284,8 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
142
284
  injectedStepSequence += 1;
143
285
  const boundedSummary = summary.trim().slice(0, 120) || '外部工具已完成,继续处理当前任务。';
144
286
  try {
145
- ;
287
+ if (on !== undefined)
288
+ pendingStepTransitions.add(sessionId);
146
289
  agent.inject({
147
290
  id: `codingns-external-step-${injectedStepSequence}-${randomUUID()}`,
148
291
  role: 'user',
@@ -154,6 +297,7 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
154
297
  return true;
155
298
  }
156
299
  catch {
300
+ pendingStepTransitions.delete(sessionId);
157
301
  return false;
158
302
  }
159
303
  };
@@ -222,6 +366,9 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
222
366
  appendUsageSample(sessionId, usage) {
223
367
  return appendNativeUsageSample(sessionId, usage);
224
368
  },
369
+ appendCompactionEvent(sessionId, event) {
370
+ return appendNativeCompactionEvent(sessionId, event);
371
+ },
225
372
  canInjectNextStep(sessionId) {
226
373
  return canInjectNativeNextStep(sessionId);
227
374
  },
@@ -319,7 +466,15 @@ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
319
466
  subscribe(handlers) {
320
467
  const disposers = [];
321
468
  if (on !== undefined && handlers.onEvent !== undefined) {
322
- disposers.push(on('session/event', (session, event) => handlers.onEvent?.(session, event)));
469
+ disposers.push(on('session/event', (session, event) => {
470
+ if (isNativeStepStart(event)) {
471
+ for (const sessionId of pendingStepTransitions) {
472
+ if (store?.get(sessionId) === session)
473
+ pendingStepTransitions.delete(sessionId);
474
+ }
475
+ }
476
+ handlers.onEvent?.(session, event);
477
+ }));
323
478
  }
324
479
  if (on !== undefined && handlers.onFlush !== undefined) {
325
480
  disposers.push(on('session/flush', (session) => handlers.onFlush?.(session)));
@@ -336,6 +491,9 @@ function appendableSession(value) {
336
491
  return null;
337
492
  return value;
338
493
  }
494
+ function isNativeStepStart(value) {
495
+ return isRecord(value) && value.type === 'step/start';
496
+ }
339
497
  function latestRequestContext(session) {
340
498
  let events;
341
499
  try {
@@ -362,6 +520,37 @@ function latestRequestContext(session) {
362
520
  }
363
521
  return null;
364
522
  }
523
+ function historicalUsageContextWindow(session) {
524
+ let events;
525
+ try {
526
+ events = session.snapshotEvents();
527
+ }
528
+ catch {
529
+ return undefined;
530
+ }
531
+ const counts = new Map();
532
+ for (const candidate of events) {
533
+ const event = isRecord(candidate) && candidate.type === 'assistant/attempt' ? candidate : null;
534
+ const data = isRecord(event?.data) ? event.data : null;
535
+ const stream = Array.isArray(data?.stream) ? data.stream : [];
536
+ for (const entry of stream) {
537
+ const chunk = isRecord(entry) && isRecord(entry.chunk) ? entry.chunk : null;
538
+ const usage = isRecord(chunk?.usage) ? chunk.usage : null;
539
+ const window = usage?.contextWindow;
540
+ if (typeof window === 'number' && Number.isFinite(window) && window > 0)
541
+ counts.set(window, (counts.get(window) ?? 0) + 1);
542
+ }
543
+ }
544
+ let selected;
545
+ let count = 0;
546
+ for (const [window, occurrences] of counts) {
547
+ if (occurrences >= count) {
548
+ selected = window;
549
+ count = occurrences;
550
+ }
551
+ }
552
+ return selected;
553
+ }
365
554
  function activeStep(session) {
366
555
  let events;
367
556
  try {
@@ -390,6 +579,45 @@ function activeStep(session) {
390
579
  function eventSeq(value) {
391
580
  return isRecord(value) ? finiteInteger(value.seq) : null;
392
581
  }
582
+ function compactionSurfaceRange(session) {
583
+ let nodes = [];
584
+ try {
585
+ if (Array.isArray(session.surface?.nodes))
586
+ nodes = session.surface.nodes;
587
+ if (nodes.length === 0) {
588
+ nodes = session.snapshotEvents().flatMap((candidate) => {
589
+ if (!isRecord(candidate) || typeof candidate.seq !== 'number' || candidate.type === 'system/message')
590
+ return [];
591
+ const op = candidate.surfaceOp;
592
+ return op === 'append' ? [candidate.seq] : [];
593
+ });
594
+ }
595
+ const events = session.snapshotEvents();
596
+ let usable = nodes.filter((seq) => {
597
+ const event = session.eventAt?.(seq) ?? events[seq];
598
+ return !(isRecord(event) && event.type === 'system/message');
599
+ });
600
+ // 当前回合刚追加的 user/message 不是历史压缩对象,保留它避免把用户本轮
601
+ // 输入一并替换;旧 assistant/tool 节点仍可作为压缩范围。
602
+ const last = usable.at(-1);
603
+ const lastEvent = last === undefined ? undefined : session.eventAt?.(last) ?? events[last];
604
+ if (last !== undefined && isRecord(lastEvent) && lastEvent.type === 'user/message')
605
+ usable = usable.slice(0, -1);
606
+ if (usable.length === 0)
607
+ return null;
608
+ return { start: usable[0], end: usable.at(-1), seqs: usable };
609
+ }
610
+ catch {
611
+ return null;
612
+ }
613
+ }
614
+ function boundCompactionSummary(summary) {
615
+ const value = summary?.trim() || 'Codex 已压缩较早的上下文。';
616
+ return value.slice(0, 120);
617
+ }
618
+ function safeCount(value) {
619
+ return value !== undefined && Number.isSafeInteger(value) && value >= 0 ? value : 0;
620
+ }
393
621
  function finiteInteger(value) {
394
622
  return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null;
395
623
  }
@@ -358,7 +358,7 @@ export function createLocalDshWebRuntimeProvider(options) {
358
358
  if (input.headers !== undefined)
359
359
  init.headers = Object.fromEntries(input.headers);
360
360
  if (input.body !== undefined)
361
- init.body = input.body;
361
+ init.body = normalizeConnectionRpcBody(path, input.method ?? 'GET', input.body);
362
362
  await ensureAuthenticated();
363
363
  const response = await fetchLocal(path, init);
364
364
  debug.log('web.provider.request.response', { sessionId: session.sessionId, path, method: input.method ?? 'GET', status: response.status });
@@ -409,6 +409,31 @@ function getSetCookie(headers) {
409
409
  }
410
410
  return undefined;
411
411
  }
412
+ /**
413
+ * DSH Gateway 的 Connection RPC 要求请求参数位于 payload.args。
414
+ * 旧版 DSH Web Client 曾把单个参数对象直接放进 payload;只在 `/api/*`
415
+ * 的 POST RPC 请求上补这一层,已经符合协议的请求保持字节级语义不变。
416
+ */
417
+ function normalizeConnectionRpcBody(path, method, body) {
418
+ if (method.toUpperCase() !== 'POST' || !path.startsWith('/api/'))
419
+ return body;
420
+ try {
421
+ const message = JSON.parse(body);
422
+ if (!isOfficialConnectionRpcMessage(message) || !isRecord(message.payload) || 'args' in message.payload)
423
+ return body;
424
+ return JSON.stringify({ ...message, payload: { args: message.payload } });
425
+ }
426
+ catch {
427
+ return body;
428
+ }
429
+ }
430
+ /** 只兼容 DSH 官方两段式 Remote endpoint,不能改写 CodingNS 自有 RPC。 */
431
+ function isOfficialConnectionRpcMessage(value) {
432
+ if (!isRecord(value) || value.type !== 'client-request' || typeof value.method !== 'string')
433
+ return false;
434
+ const parts = value.method.split('/');
435
+ return parts.length === 2 && parts.every((part) => part !== '') && parts[0] !== 'codingns';
436
+ }
412
437
  class RemoteWebRuntimeError extends Error {
413
438
  code;
414
439
  constructor(code, message) {
@@ -179,7 +179,9 @@ const CODINGNS_RPC_ENDPOINTS = [
179
179
  'debug/runtime/get', 'debug/runtime/list', 'debug/runtime/stop',
180
180
  'debug/port/check', 'debug/port/terminate', 'debug/port/kill', 'debug/proxy/get', 'debug/proxy/enable', 'debug/proxy/disable',
181
181
  'git/status', 'git/init', 'git/diff', 'git/stage', 'git/unstage', 'git/discard', 'git/commit', 'git/commit-diff', 'git/history', 'git/branches', 'git/switch', 'git/fetch', 'git/pull', 'git/push', 'git/undo',
182
- 'lanAccessDsh/addresses', 'lanAccessDsh/detect', 'lanAccessDsh/get', 'lanAccessDsh/settings/get', 'lanAccessDsh/settings/set', 'lanAccessDsh/login/get', 'lanAccessDsh/login/set', 'lanAccessDsh/login/session/open', 'lanAccessDsh/start', 'lanAccessDsh/stop',
182
+ 'fileManagement/session-changes', 'fileManagement/read', 'fileManagement/download', 'fileManagement/write', 'fileManagement/create-file', 'fileManagement/create-directory', 'fileManagement/rename', 'fileManagement/copy', 'fileManagement/move', 'fileManagement/delete', 'fileManagement/git-ignore',
183
+ 'lanAccessDsh/addresses', 'lanAccessDsh/detect', 'lanAccessDsh/get', 'lanAccessDsh/settings/get', 'lanAccessDsh/settings/set', 'lanAccessDsh/login/get', 'lanAccessDsh/login/set', 'lanAccessDsh/login/session/open', 'lanAccessDsh/login/session/refresh', 'lanAccessDsh/start', 'lanAccessDsh/stop',
184
+ 'peerHost/list', 'peerHost/diagnostics', 'peerHost/create', 'peerHost/update', 'peerHost/remove', 'peerHost/check', 'peerHost/reconnect', 'peerHost/login', 'peerHost/logout', 'peerHost/request', 'peerHost/wsEndpoint', 'peerHost/aggregate',
183
185
  'cli/catalog', 'cli/models', 'cli/adapter/set', 'cli/session/get', 'cli/session/set', 'cli/session/list', 'cli/session/adapter-map', 'cli/session/archive', 'cli/session/steer', 'cli/session/follow-up', 'cli/session/interrupt', 'cli/subscription',
184
186
  ];
185
187
  /** 创建远程设置处理器;只允许 Codingns4DSH 自己的 namespace 和路径编辑。 */
@@ -249,9 +251,12 @@ function isAllowedSettingsPath(path) {
249
251
  if (path.length === 1)
250
252
  return ['controlBaseUrl', 'controlBaseUrls', 'terminalEnhancement', 'workspaceSessionEnhancement'].includes(path[0] ?? '');
251
253
  if (path[0] === 'modules')
252
- return path.length === 2 && ['lanAccess', 'reverseProxy', 'cliAdapters', 'terminalEnhancement', 'workspaceSessionEnhancement', 'debug', 'gitManagement'].includes(path[1] ?? '');
254
+ return path.length === 2 && ['lanAccess', 'reverseProxy', 'cliAdapters', 'terminalEnhancement', 'workspaceSessionEnhancement', 'debug', 'gitManagement', 'fileManagement', 'peerHost'].includes(path[1] ?? '');
253
255
  if (path[0] === 'workspaceSessionEnhancement') {
254
- return path.length === 2 && ['showAdapterLogo', 'showArchivedSessions', 'showWorkspaceHiding', 'hiddenWorkspaceIds', 'showSubscriptionUsage', 'showQuickPhrases', 'quickPhrases', 'quickPhrasesSeeded'].includes(path[1] ?? '');
256
+ return path.length === 2 && ['showAdapterLogo', 'showArchivedSessions', 'showWorkspaceHiding', 'hiddenWorkspaceIds', 'showSubscriptionUsage', 'showQuickPhrases', 'rememberConversationRightbarRatio', 'quickPhrases', 'quickPhrasesSeeded'].includes(path[1] ?? '');
257
+ }
258
+ if (path[0] === 'fileManagement') {
259
+ return path.length === 2 && ['menuEnhancement', 'fileEditor', 'sessionChangedFiles'].includes(path[1] ?? '');
255
260
  }
256
261
  return path[0] === 'lanAccessDsh' && path.length === 2 && ['autoStart', 'listenHost', 'listenPort', 'dshPort'].includes(path[1] ?? '');
257
262
  }
@@ -0,0 +1,5 @@
1
+ import type { SessionChangedFiles } from '../shared/contracts/file-management.js';
2
+ import type { CodingNsHostServices } from './features/types.js';
3
+ /** 优先从 DSH 原生 Session 事件读取,原生事件不可用时回退到 JSONL。 */
4
+ export declare function readSessionChangedFiles(services: CodingNsHostServices, sessionId: string, workspaceRoot: string): Promise<SessionChangedFiles>;
5
+ //# sourceMappingURL=session-changes.d.ts.map
@@ -0,0 +1,163 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { isAbsolute, normalize, relative, resolve } from 'node:path';
3
+ const PATH_KEYS = new Set([
4
+ 'path', 'paths', 'filePath', 'file_path', 'srcPath', 'src_path',
5
+ 'dstPath', 'dst_path', 'oldPath', 'old_path', 'newPath', 'new_path',
6
+ 'sourcePath', 'source_path', 'targetPath', 'target_path',
7
+ ]);
8
+ /** 优先从 DSH 原生 Session 事件读取,原生事件不可用时回退到 JSONL。 */
9
+ export async function readSessionChangedFiles(services, sessionId, workspaceRoot) {
10
+ let session = services.nativeSessions?.get(sessionId);
11
+ if (session === undefined && services.nativeSessions?.listRemote !== undefined) {
12
+ try {
13
+ const candidates = await services.nativeSessions.listRemote();
14
+ session = candidates.find((candidate) => sessionIdentity(candidate) === sessionId);
15
+ }
16
+ catch {
17
+ // 原生列表接口不可用时继续走 JSONL 降级。
18
+ }
19
+ }
20
+ const root = resolve(workspaceRoot);
21
+ const nativeEvents = snapshotEvents(session);
22
+ const nativePaths = extractToolPaths(nativeEvents, root);
23
+ if (nativePaths.size > 0)
24
+ return { paths: [...nativePaths].sort() };
25
+ const jsonlPath = findJsonlReference(session);
26
+ if (jsonlPath === undefined)
27
+ return { paths: [] };
28
+ try {
29
+ const content = await readFile(jsonlPath, 'utf8');
30
+ const events = content.split(/\r?\n/u).flatMap((line) => {
31
+ try {
32
+ const parsed = JSON.parse(line);
33
+ return [parsed];
34
+ }
35
+ catch {
36
+ return [];
37
+ }
38
+ });
39
+ return { paths: [...extractToolPaths(events, root)].sort() };
40
+ }
41
+ catch {
42
+ // JSONL 是降级数据源;文件不存在或不可读时保持可解释的空结果。
43
+ return { paths: [] };
44
+ }
45
+ }
46
+ function snapshotEvents(value) {
47
+ if (!isRecord(value))
48
+ return [];
49
+ if (typeof value.snapshotEvents === 'function') {
50
+ try {
51
+ const events = value.snapshotEvents();
52
+ if (Array.isArray(events))
53
+ return events;
54
+ }
55
+ catch {
56
+ // 单个会话损坏时继续尝试其内嵌事件数组。
57
+ }
58
+ }
59
+ return Array.isArray(value.events) ? value.events : [];
60
+ }
61
+ function extractToolPaths(events, workspaceRoot) {
62
+ const paths = new Set();
63
+ for (const candidate of events) {
64
+ const event = isRecord(candidate) ? candidate : undefined;
65
+ if (event === undefined)
66
+ continue;
67
+ const data = isRecord(event.data) ? event.data : event;
68
+ if (event.type === 'tool/call') {
69
+ collectInputPaths(data.arguments, workspaceRoot, paths);
70
+ continue;
71
+ }
72
+ if (event.type === 'tool/result') {
73
+ collectInputPaths(data.meta, workspaceRoot, paths);
74
+ continue;
75
+ }
76
+ if (event.type !== 'assistant/message')
77
+ continue;
78
+ const message = isRecord(data.message) ? data.message : undefined;
79
+ const content = message?.content;
80
+ if (!Array.isArray(content))
81
+ continue;
82
+ for (const block of content) {
83
+ if (!isRecord(block) || block.type !== 'tool-call')
84
+ continue;
85
+ collectInputPaths(block.arguments, workspaceRoot, paths);
86
+ }
87
+ }
88
+ return paths;
89
+ }
90
+ function collectInputPaths(value, workspaceRoot, paths) {
91
+ if (typeof value !== 'string') {
92
+ if (Array.isArray(value)) {
93
+ for (const item of value)
94
+ collectInputPaths(item, workspaceRoot, paths);
95
+ return;
96
+ }
97
+ if (isRecord(value)) {
98
+ for (const [key, nested] of Object.entries(value)) {
99
+ if (typeof nested === 'string' && PATH_KEYS.has(key))
100
+ addPath(nested, workspaceRoot, paths);
101
+ else
102
+ collectInputPaths(nested, workspaceRoot, paths);
103
+ }
104
+ }
105
+ return;
106
+ }
107
+ const trimmed = value.trim();
108
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
109
+ for (const line of trimmed.split(/\r?\n/u)) {
110
+ const match = /^\*\*\* (?:Update|Add|Delete) File:\s+(.+)$/u.exec(line)
111
+ ?? /^\*\*\* Move to:\s+(.+)$/u.exec(line);
112
+ if (match?.[1] !== undefined)
113
+ addPath(match[1], workspaceRoot, paths);
114
+ }
115
+ return;
116
+ }
117
+ try {
118
+ collectInputPaths(JSON.parse(trimmed), workspaceRoot, paths);
119
+ }
120
+ catch {
121
+ // 工具参数不是 JSON 时只使用 apply_patch 文本格式。
122
+ }
123
+ }
124
+ function addPath(value, workspaceRoot, paths) {
125
+ const trimmed = value.trim().replace(/^['"]+|['"]+$/gu, '');
126
+ if (trimmed === '' || trimmed === '.' || trimmed === './')
127
+ return;
128
+ const candidate = isAbsolute(trimmed) ? normalize(trimmed) : resolve(workspaceRoot, trimmed);
129
+ const relativePath = relative(workspaceRoot, candidate).replaceAll('\\', '/');
130
+ if (relativePath === '' || relativePath.startsWith('../') || isAbsolute(relativePath))
131
+ return;
132
+ paths.add(relativePath.replace(/^\.\//u, ''));
133
+ }
134
+ function findJsonlReference(value, depth = 0) {
135
+ if (depth > 5 || !isRecord(value))
136
+ return undefined;
137
+ for (const key of ['rawStoreRef', 'jsonlPath', 'sessionPath', 'filePath']) {
138
+ const candidate = value[key];
139
+ if (typeof candidate === 'string' && /\.jsonl(?:\.zstd)?$/u.test(candidate.trim()))
140
+ return candidate.trim();
141
+ }
142
+ for (const nested of Object.values(value)) {
143
+ const result = findJsonlReference(nested, depth + 1);
144
+ if (result !== undefined)
145
+ return result;
146
+ }
147
+ return undefined;
148
+ }
149
+ function sessionIdentity(value) {
150
+ if (!isRecord(value))
151
+ return undefined;
152
+ for (const key of ['sessionId', 'id']) {
153
+ const candidate = value[key];
154
+ if (typeof candidate === 'string' && candidate.trim() !== '')
155
+ return candidate.trim();
156
+ }
157
+ const header = value.header;
158
+ return isRecord(header) && typeof header.id === 'string' ? header.id.trim() : undefined;
159
+ }
160
+ function isRecord(value) {
161
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
162
+ }
163
+ //# sourceMappingURL=session-changes.js.map
@@ -60,6 +60,7 @@ export const CodingNsSettingsSchema = z.object({
60
60
  hiddenWorkspaceIds: z.array(z.string().min(1).max(512)).default(DEFAULT_CODINGNS_SETTINGS.workspaceSessionEnhancement.hiddenWorkspaceIds),
61
61
  showSubscriptionUsage: z.boolean().default(DEFAULT_CODINGNS_SETTINGS.workspaceSessionEnhancement.showSubscriptionUsage),
62
62
  showQuickPhrases: z.boolean().default(DEFAULT_CODINGNS_SETTINGS.workspaceSessionEnhancement.showQuickPhrases),
63
+ rememberConversationRightbarRatio: z.boolean().default(DEFAULT_CODINGNS_SETTINGS.workspaceSessionEnhancement.rememberConversationRightbarRatio),
63
64
  quickPhrases: z.array(z.object({
64
65
  id: z.string().min(1).max(128),
65
66
  text: z.string().min(1).max(4000),
@@ -67,6 +68,11 @@ export const CodingNsSettingsSchema = z.object({
67
68
  // 缺少该字段说明是旧配置;Client 首次加载时会补齐内置快捷会话。
68
69
  quickPhrasesSeeded: z.boolean().default(false),
69
70
  }).default(DEFAULT_CODINGNS_SETTINGS.workspaceSessionEnhancement),
71
+ fileManagement: z.object({
72
+ menuEnhancement: z.boolean().default(DEFAULT_CODINGNS_SETTINGS.fileManagement.menuEnhancement),
73
+ fileEditor: z.boolean().default(DEFAULT_CODINGNS_SETTINGS.fileManagement.fileEditor),
74
+ sessionChangedFiles: z.boolean().default(DEFAULT_CODINGNS_SETTINGS.fileManagement.sessionChangedFiles),
75
+ }).default(DEFAULT_CODINGNS_SETTINGS.fileManagement),
70
76
  });
71
77
  /**
72
78
  * DSH 0.1.7 只会把 `volatile` 配置投影成可编辑 ConfigForm。
@@ -20,7 +20,7 @@ export { createHttpDshH5ControlApi, startDshH5BrowserBootstrap } from './client/
20
20
  export type { DshH5BrowserBootstrapOptions, DshH5BrowserBootstrapResult, DshH5BrowserControlApi } from './client/dsh-h5-bootstrap.js';
21
21
  export { CODINGNS_RPC_CHANNEL } from './shared/contracts/transport.js';
22
22
  export { CODINGNS4DSH_DEBUG_ENV, debugInfo, debugWarn, resolveCodingNsDebugEnabled, } from './shared/debug.js';
23
- export { CODINGNS_CONTROL_BASE_URL_FIELD, CODINGNS_CONTROL_BASE_URLS_FIELD, CODINGNS_LAN_ACCESS_DSH_FIELD, CODINGNS_MODULES_FIELD, CODINGNS_SETTINGS_NAMESPACE, DEFAULT_CODINGNS_CONTROL_BASE_URL, DEFAULT_CODINGNS_CONTROL_BASE_URLS, DEFAULT_CODINGNS_SETTINGS, enabledFeatureNames, isFeatureEnabled, } from './shared/contracts/config.js';
23
+ export { CODINGNS_CONTROL_BASE_URL_FIELD, CODINGNS_CONTROL_BASE_URLS_FIELD, CODINGNS_LAN_ACCESS_DSH_FIELD, CODINGNS_FILE_MANAGEMENT_FIELD, CODINGNS_MODULES_FIELD, CODINGNS_SETTINGS_NAMESPACE, DEFAULT_CODINGNS_CONTROL_BASE_URL, DEFAULT_CODINGNS_CONTROL_BASE_URLS, DEFAULT_CODINGNS_SETTINGS, DEFAULT_FILE_MANAGEMENT_SETTINGS, enabledFeatureNames, isFeatureEnabled, } from './shared/contracts/config.js';
24
24
  export type * from './shared/index.js';
25
25
  export * from './dsh-capabilities/index.js';
26
26
  export { dispatchCodingNsRpc, type CodingNsRpcContext, type CodingNsRpcDispatchResult } from './dsh-capabilities/host/connection-rpc-adapter.js';
@@ -18,7 +18,7 @@ export { RemoteDshWebContext } from './client/remote-web-context.js';
18
18
  export { createHttpDshH5ControlApi, startDshH5BrowserBootstrap } from './client/dsh-h5-bootstrap.js';
19
19
  export { CODINGNS_RPC_CHANNEL } from './shared/contracts/transport.js';
20
20
  export { CODINGNS4DSH_DEBUG_ENV, debugInfo, debugWarn, resolveCodingNsDebugEnabled, } from './shared/debug.js';
21
- export { CODINGNS_CONTROL_BASE_URL_FIELD, CODINGNS_CONTROL_BASE_URLS_FIELD, CODINGNS_LAN_ACCESS_DSH_FIELD, CODINGNS_MODULES_FIELD, CODINGNS_SETTINGS_NAMESPACE, DEFAULT_CODINGNS_CONTROL_BASE_URL, DEFAULT_CODINGNS_CONTROL_BASE_URLS, DEFAULT_CODINGNS_SETTINGS, enabledFeatureNames, isFeatureEnabled, } from './shared/contracts/config.js';
21
+ export { CODINGNS_CONTROL_BASE_URL_FIELD, CODINGNS_CONTROL_BASE_URLS_FIELD, CODINGNS_LAN_ACCESS_DSH_FIELD, CODINGNS_FILE_MANAGEMENT_FIELD, CODINGNS_MODULES_FIELD, CODINGNS_SETTINGS_NAMESPACE, DEFAULT_CODINGNS_CONTROL_BASE_URL, DEFAULT_CODINGNS_CONTROL_BASE_URLS, DEFAULT_CODINGNS_SETTINGS, DEFAULT_FILE_MANAGEMENT_SETTINGS, enabledFeatureNames, isFeatureEnabled, } from './shared/contracts/config.js';
22
22
  export * from './dsh-capabilities/index.js';
23
23
  export { dispatchCodingNsRpc } from './dsh-capabilities/host/connection-rpc-adapter.js';
24
24
  export { FeatureRegistry, FeatureRegistryError, FeatureResourceScopeImpl, } from './features/registry.js';