dsh-vscode-mode 0.9.0 → 0.10.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,6 @@
1
1
  {
2
2
  "name": "dsh-vscode-mode",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "DSH 上的类 VSCode 编码体验:Monaco 编辑器(文件页签/QuickOpen/状态栏,可驻 DSH 0.1.5+ 官方右侧 Sidebar 与对话同屏)+ 命令栏(Ctrl+Shift+P)与可扩展指令系统 + VS Code 兼容代码片段(.code-snippets 全局/项目,IntelliSense 展开)+ Agent 编辑差异审查(整文件差异/采纳/拒绝/归档/回滚)+ SVN 集成(侧栏变更面板/日志弹窗/并排差异)+ 语言服务器(LSP)智能(跳转定义/引用/hover/大纲 + VSIX 扩展市场安装),状态持久化到工作区旁车",
5
5
  "icon": "assets/icon.svg",
6
6
  "keywords": [
@@ -285,6 +285,17 @@ function mintRpcId(): string {
285
285
  }
286
286
  }
287
287
 
288
+ /**
289
+ * wire 槽实参组装(gateway `invoke` 铁证:`request.args[parameter.wire]` 具名槽,
290
+ * `session/prompt` 的槽名 = "request",值 = RpcRequest `{rpcId, payload}`)。
291
+ * @author ddj 2026年09月24号
292
+ * @param payload 业务载荷
293
+ * @returns 具名槽实参 `{ args: { request: RpcRequest } }`
294
+ */
295
+ function slotArg(payload: object): { args: { request: { rpcId: string; payload: object } } } {
296
+ return { args: { request: { rpcId: mintRpcId(), payload } } }
297
+ }
298
+
288
299
  /**
289
300
  * RPC 返回解析(照官方 dsh-client-runtime 调用形态:`const { result } = await api.sessions.create(...)`)。
290
301
  * 官方 api.sessions 面返回**信封** `{ rpcId, result: RpcResult }`——须解 .result;
@@ -335,6 +346,7 @@ async function apiSessionCreate(cwd: string | undefined): Promise<string> {
335
346
  */
336
347
  export async function apiSessionPrompt(sessionId: string, text: string): Promise<boolean> {
337
348
  try {
349
+ const payload = { sessionId, mode: 'queue', content: [{ type: 'text', text }] }
338
350
  const res = await fetch(SESSION_PROMPT_PATH, {
339
351
  method: 'POST',
340
352
  headers: { 'content-type': 'application/json' },
@@ -342,7 +354,7 @@ export async function apiSessionPrompt(sessionId: string, text: string): Promise
342
354
  type: 'client-request',
343
355
  rpcId: mintRpcId(),
344
356
  method: 'session/prompt',
345
- payload: { sessionId, mode: 'queue', content: [{ type: 'text', text }] },
357
+ payload: slotArg(payload),
346
358
  }),
347
359
  })
348
360
  const json = await res.json().catch(() => null) as { result?: { ok?: boolean } } | null
@@ -362,25 +374,10 @@ export async function apiSessionPrompt(sessionId: string, text: string): Promise
362
374
  * @returns 新会话结果(含 id)
363
375
  */
364
376
  export async function newDraftSession(ctx: CtxLike, cwd: string | undefined): Promise<DraftSessionResult> {
365
- // 主路径:wire 直调
366
- try {
367
- const id = await apiSessionCreate(cwd)
368
- if (id) return { ok: true, id }
369
- } catch { /* wire 不可用:回落 service 面 */ }
377
+ // 主路径:client store manager 面(官方 connectWorkspace 同链路)——create 自带 store upsert,
378
+ // 「on resolution the child is in the list store and open() can target it」;
379
+ // wire 直调虽能建会话但 store 无感,故只作最后兜底。
370
380
  try {
371
- // 回落 1:remote.session service 面(官方返回信封 {rpcId, result},见 rpcOk;实参双形态兼容)
372
- const remote = ctx.get('remote.session') as {
373
- create?: (req: unknown) => Promise<unknown>
374
- } | undefined
375
- if (typeof remote?.create === 'function') {
376
- const payload = cwd ? { cwd } : {}
377
- for (const arg of [payload, { rpcId: mintRpcId(), payload }]) {
378
- const r = rpcOk(await remote.create(arg))
379
- const id = r.value as { sessionId?: unknown } | undefined
380
- if (r.ok && typeof id?.sessionId === 'string') return { ok: true, id: id.sessionId }
381
- }
382
- }
383
- // 回落 2:旧版 client store 形态(id / {id} / {sessionId} / {session:{id}} 四解)
384
381
  const sessions = ctx.get('sessions') as { create?: (opts?: { cwd?: string }) => unknown } | undefined
385
382
  if (typeof sessions?.create === 'function') {
386
383
  const created = await sessions.create(cwd ? { cwd } : undefined)
@@ -392,6 +389,25 @@ export async function newDraftSession(ctx: CtxLike, cwd: string | undefined): Pr
392
389
  : ''
393
390
  if (id) return { ok: true, id }
394
391
  }
392
+ } catch { /* 回落下一通道 */ }
393
+ try {
394
+ // 回落 1:api.sessions service 面(返回信封 {rpcId, result},见 rpcOk;多形态容错)
395
+ const remote = ctx.get('remote.session') as { create?: (req: unknown) => Promise<unknown> } | undefined
396
+ if (typeof remote?.create === 'function') {
397
+ const payload = cwd ? { cwd } : {}
398
+ for (const arg of [slotArg(payload), { request: { rpcId: mintRpcId(), payload } }, { rpcId: mintRpcId(), payload }, payload]) {
399
+ try {
400
+ const r = rpcOk(await remote.create(arg))
401
+ const id = r.value as { sessionId?: unknown } | undefined
402
+ if (r.ok && typeof id?.sessionId === 'string') return { ok: true, id: id.sessionId }
403
+ } catch { /* 单形态失败试下一形态 */ }
404
+ }
405
+ }
406
+ } catch { /* 回落 wire */ }
407
+ // 回落 2:wire 直调(创建成功但 store 无感,open 切换不保证;任务发送仍可用)
408
+ try {
409
+ const id = await apiSessionCreate(cwd)
410
+ if (id) return { ok: true, id }
395
411
  } catch { /* 全部失败:调用方降级 */ }
396
412
  return { ok: false }
397
413
  }
@@ -559,11 +575,21 @@ export function createAddToConversation(ctx: CtxLike): AddToConversation {
559
575
  const startDraftSession: AddToConversation['startDraftSession'] = async (cwd) => {
560
576
  const result = await newDraftSession(ctx, cwd)
561
577
  if (result.ok && result.id) {
562
- // 尽力切换到新会话(缺失/异常不影响结果——仅视图不切换)
563
578
  try {
564
- const sessions = ctx.get('sessions') as { open?: (id: string) => void } | undefined
565
- sessions?.open?.(result.id)
566
- } catch { /* 切换失败仅视图停留 */ }
579
+ // 主动铸造 scope(scope 对新会话惰性铸造,inputFor/composer 面依赖它)
580
+ const sessions = ctx.get('sessions') as {
581
+ materializeScope?: (id: string) => unknown
582
+ manager?: { select?: (id: string) => void; rename?: (id: string, title: string) => void }
583
+ open?: (id: string) => void
584
+ } | undefined
585
+ sessions?.materializeScope?.(result.id)
586
+ // 命名醒目标题(manager.rename 在面实证):会话列表一眼可见、点击即入
587
+ const title = 'AI 深度分析 · ' + (String(cwd ?? '').split(/[\\/]/).pop() || 'SVN 变更')
588
+ void sessions?.manager?.rename?.(result.id, title)
589
+ // 切换尝试(open/select 未对插件暴露时静默跳过:rename 已保证可见性)
590
+ void sessions?.manager?.select?.(result.id)
591
+ void sessions?.open?.(result.id)
592
+ } catch { /* 面能力缺失不致命:任务发送不受影响 */ }
567
593
  }
568
594
  return result
569
595
  }
@@ -578,18 +604,38 @@ export function createAddToConversation(ctx: CtxLike): AddToConversation {
578
604
  */
579
605
  const sendTask: AddToConversation['sendTask'] = async (sessionId, text) => {
580
606
  if (!sessionId) return false
607
+ // 主路径:**官方 composer 发送链路**(InputLike setDraft + submit —— UI「发送」的内部路径)。
608
+ // inputFor 依赖 sessions.scope(id):新会话铸造有时延,重试 3 次后再降级。
609
+ const composerOnce = async (): Promise<boolean> => {
610
+ const input = inputFor(ctx, sessionId) as { setDraft?: (t: string) => void; submit?: (mode?: unknown) => void } | undefined
611
+ if (input && typeof input.setDraft === 'function' && typeof input.submit === 'function') {
612
+ input.setDraft(text)
613
+ input.submit()
614
+ return true
615
+ }
616
+ return false
617
+ }
581
618
  try {
582
- if (await apiSessionPrompt(sessionId, text)) return true
583
- } catch { /* 回落 service 面 */ }
619
+ for (let attempt = 3; attempt > 0; attempt--) {
620
+ if (await composerOnce()) return true
621
+ if (attempt > 1) await new Promise((resolve) => setTimeout(resolve, 500))
622
+ }
623
+ } catch { /* 降级下一通道 */ }
624
+ // 兜底 1:remote.session service 面(多形态容错)
584
625
  try {
585
626
  const remote = ctx.get('remote.session') as { prompt?: (req: unknown) => Promise<unknown> } | undefined
586
627
  if (typeof remote?.prompt === 'function') {
587
- // 官方 api.sessions.prompt(request) 返回信封(rpcOk 解 .result);实参双形态兼容
588
628
  const payload = { sessionId, mode: 'queue', content: [{ type: 'text', text }] }
589
- for (const arg of [payload, { rpcId: mintRpcId(), payload }]) {
590
- if (rpcOk(await remote.prompt(arg)).ok) return true
629
+ for (const arg of [slotArg(payload), { request: { rpcId: mintRpcId(), payload } }, { rpcId: mintRpcId(), payload }, payload]) {
630
+ try {
631
+ if (rpcOk(await remote.prompt(arg)).ok) return true
632
+ } catch { /* 单形态失败试下一形态 */ }
591
633
  }
592
634
  }
635
+ } catch { /* 降级下一通道 */ }
636
+ // 兜底 2:HTTP wire
637
+ try {
638
+ return await apiSessionPrompt(sessionId, text)
593
639
  } catch { /* 全失败 */ }
594
640
  return false
595
641
  }
@@ -859,7 +859,7 @@ export function SvnPanel(props) {
859
859
  if (!id || typeof add.sendTask !== 'function') { degrade(prompt); return }
860
860
  void add.sendTask(id, prompt).then((sent) => {
861
861
  if (sent) {
862
- live?.notify?.('已在新会话发起深度分析(任务已自动发送),请在会话列表查看')
862
+ live?.notify?.('已在新会话「AI 深度分析」发起任务并自动发送,请在左侧会话列表点开查看进度')
863
863
  startDeepPoll(since)
864
864
  } else {
865
865
  degrade(prompt)