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/README.md +11 -2
- package/lib/client.js +86 -32
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/addToConversation.ts +74 -28
- package/src/client/sidebar/panels/SvnPanel.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vscode-mode",
|
|
3
|
-
"version": "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:
|
|
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
|
-
// 主路径:
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
|
|
565
|
-
sessions
|
|
566
|
-
|
|
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
|
-
|
|
583
|
-
|
|
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
|
-
|
|
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)
|