dsh-vscode-mode 0.21.3 → 0.21.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.
- package/README.md +5 -3
- package/lib/client.js +182 -76
- package/lib/client.js.map +2 -2
- package/package.json +1 -1
- package/src/client/addToConversation.ts +79 -32
- package/src/client/sidebar/panels/SvnPanel.ts +38 -30
package/README.md
CHANGED
|
@@ -477,6 +477,7 @@ DSH 每次启动监听随机端口,`http://127.0.0.1:<port>` 就是浏览器 o
|
|
|
477
477
|
|
|
478
478
|
官方 `dsh plugin` 方式,三选一。下面以 `web` profile 为例;Desktop 或其他命名
|
|
479
479
|
profile 请替换为实际目标,不要把示例目录当成当前运行 profile。升级前可阅读
|
|
480
|
+
**v0.21.4** 的任务发送结果分类与诚实草稿降级:只有明确 accepted 才等待分析方案;unknown/submitted/cancelled 不另填草稿或重发,安全降级只填实际当前会话草稿并提示手动发送。旧 boolean 接口保留,legacy true 不等于受理。开发 link 客户端 rev458288ddb012 的真实公开入口与目标受理已验;完整 SVN 分析/方案投递因 `SetNamedSecurityInfoW failed (Win32 5)` 未完成,用户仅本批接受此缺口,不代表 Host 整体升级或当前实例已安装 npm 0.21.4。见 [0.21.4 发布说明](docs/release-0.21.4.md)。以下保留历史版本事实:
|
|
480
481
|
**v0.21.3** 的 MCP 初始化失败结果与安全收尾解耦:已进入 startup 的调用者在失败或截止时间到达时及时收到拒绝,不再等待旧资源收尾;旧 work/close 未确认完成前仍占冷启动名额、阻止同 key 重建,确认收尾后才开始冷却。首次偶发超时根因仍未知,排队和其他屏障仍可能无界;本版当前 Host/GUI 未部署未验证,用户仅本批接受官方实装隔离模块与 JS 套件替代验收,见 [0.21.3 发布说明](docs/release-0.21.3.md)。官方 bridge 隔离 fixture 同步迁移到当前公开 native 执行器及批调用契约,不代表生产执行器故障修复。以下为历史版本事实,不代表本版现场验收:
|
|
481
482
|
**v0.21.2** 的新版 DSH 会话 Controller 与 MCP 公开工具目录适配:任务发送按目标会话持有生命周期,受理不明时不跨通道重发;文件范围引用的剪贴板与降级文本保留行区间;MCP 初始化失败增加脱敏持久化诊断。当前 Web Host 已完成真实 CodeGraph 调用,但首次连接偶发超时根因尚未证实,不宣称已修复,见 [0.21.2 发布说明](docs/release-0.21.2.md)。保留以下历史版本能力与验收边界:
|
|
482
483
|
**v0.21.1** 修复缺失历史文件:按完整快照显示整文件删除,Keep 保留删除、Undo 安全恢复快照,预览只读且拒绝覆盖重新出现的文件;真实 GUI 验收缺口已明确保留,见 [0.21.1 发布说明](docs/release-0.21.1.md)。保留 **v0.21.0** 新增 XLSX 基础编辑与浏览器导出副本,原件不自动写回,高级应用通过官方 Host 打开;官方差异定位统一 BOM 与 LF/CRLF/混合行尾口径,并区分内容缺失和多处命中。定位修复不扩展 CRLF patch Undo 能力,目标官方记录 ↑/↓ 的真实点击验收仍保留缺口。保留 **v0.20.5** 跨 DSH 实例只读互通——桌面端与 Web 端各自运行独立宿主时,官方审查列表/详情与工作区只读接口改按持久化会话头的 cwd 解析,另一实例创建的会话也能只读打开官方差异栏,写入类接口仍要求本端活跃会话。保留 **v0.20.4** 统一两套差异栏外观——非官方记录栏角标由内部术语「侧车记录」改为「历史改动」,悬停说明 Keep 只标记已看过、不改写磁盘,Undo 逐项校验后写回,两者都不影响官方审查;官方元数据不可用时给出可读原因与重试按钮,不再静默降级;「会话没在当前端激活」由红色告警改为中性说明。保留 v0.20.3 修复官方差异全部无法定位时差异栏无提示、上下箭头静默禁用(显示「无可跳转」与失效原因,Keep 说明只标记已采纳、不改写磁盘,会话缺失等宿主错误改为可读提示)、v0.20.2 的 MCP 状态告警收敛与原生确认弹窗,以及 v0.20.1 的文件内差异展示、块级/文件级安全审查和跨分页批量操作。升级后重启 DSH 并刷新页面;持久化与回退边界见 [0.20.0 发布说明](docs/release-0.20.0.md);从 0.14.2 或更旧版本升级还须阅读
|
|
@@ -484,13 +485,13 @@ profile 请替换为实际目标,不要把示例目录当成当前运行 profi
|
|
|
484
485
|
|
|
485
486
|
```bash
|
|
486
487
|
# ① Git 安装(clone + prepare 构建;推荐打固定 tag)
|
|
487
|
-
dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.21.
|
|
488
|
+
dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.21.4
|
|
488
489
|
|
|
489
490
|
# ② npm 注册表(发布到 npm 后)
|
|
490
|
-
dsh plugin --profile web add dsh-vscode-mode@0.21.
|
|
491
|
+
dsh plugin --profile web add dsh-vscode-mode@0.21.4
|
|
491
492
|
|
|
492
493
|
# ③ GitHub Release tgz 直装(Release asset 可用后)
|
|
493
|
-
dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.21.
|
|
494
|
+
dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.21.4/dsh-vscode-mode-0.21.4.tgz
|
|
494
495
|
```
|
|
495
496
|
|
|
496
497
|
> **DSH 版本政策(0.17.0 起)**:六项 DSH peer 仍统一声明为 `*`,另以
|
|
@@ -831,6 +832,7 @@ Keep All / Undo All 却是亮的。修复:单文件 Keep / Undo 覆盖冲突
|
|
|
831
832
|
完整变更见 [GitHub Releases](https://github.com/Lenonss/DSH_VsCodeMode/releases)。
|
|
832
833
|
近期关键版本:
|
|
833
834
|
|
|
835
|
+
- **v0.21.4**:区分任务明确受理、拒绝、不明、仅提交与取消;只有 accepted 开始等待,unknown/submitted/cancelled 不另填草稿、重试或轮询。安全降级只填实际当前会话草稿并提示手动发送,缺详细 API 明确本次未自动发送;旧 boolean 兼容但 true 不等于受理。真实开发客户端公开按钮→目标受理已验,完整 SVN 分析/方案投递因 Win32 5 未验,用户仅本批接受;Host 未重启,不自动部署。见 [发布说明](docs/release-0.21.4.md)。
|
|
834
836
|
- **v0.21.3**:MCP 初始化失败时分离调用者结果与安全收尾,已进入 startup 的调用者及时收到失败;旧 work/close 未完成仍占槽并阻止同 key 重建,清理失败不开放替代连接,收尾完成才开始冷却。不宣称解决首次偶发超时、gamma 排队或整体流程有界。当前 Host/GUI 未部署未验证,用户仅本批接受替代验收;不自动部署或重启。见 [发布说明](docs/release-0.21.3.md)。
|
|
835
837
|
- **v0.21.2**:适配新版 DSH Session Controller 与 MCP 公开工具目录;固定目标任务发送持有会话直到完成或卸载,受理不明时不跨通道重发。范围文件引用的模型文本、剪贴板与纯文本降级保持一致。MCP 初始化失败记录阶段、耗时和脱敏错误链;迁移官方 native/PTC 执行器集成测试并增加可选真实 CodeGraph stdio 验证。当前 Web Host 真实工具调用已通过,首次连接偶发超时根因仍未证实;不自动部署或重启。见 [发布说明](docs/release-0.21.2.md)。
|
|
836
838
|
- **v0.21.1**:缺失历史文件读取完整快照并整文件显示删除行;Keep 保留删除,Undo 安全恢复当前展示快照,不覆盖重新出现的文件。删除预览只读,单文件和批量操作统一处理,记录变化或证据不足时停止。真实 GUI 验收缺口已获用户接受;发布不自动部署,升级后重启 DSH 并刷新页面。见 [发布说明](docs/release-0.21.1.md)。
|
package/lib/client.js
CHANGED
|
@@ -384430,7 +384430,23 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
384430
384430
|
}
|
|
384431
384431
|
};
|
|
384432
384432
|
}
|
|
384433
|
-
/** @private @author ddj 2026年10月
|
|
384433
|
+
/** @private @author ddj 2026年10月29号 @param receipt Raw selected-channel receipt. @returns Explicit admission evidence and the historical boolean. */
|
|
384434
|
+
function taskReceipt(receipt) {
|
|
384435
|
+
const ok = receipt?.ok;
|
|
384436
|
+
if (ok === true) return {
|
|
384437
|
+
status: "accepted",
|
|
384438
|
+
legacyOk: true
|
|
384439
|
+
};
|
|
384440
|
+
if (ok === false) return {
|
|
384441
|
+
status: "rejected",
|
|
384442
|
+
legacyOk: false
|
|
384443
|
+
};
|
|
384444
|
+
return {
|
|
384445
|
+
status: "unknown",
|
|
384446
|
+
legacyOk: false
|
|
384447
|
+
};
|
|
384448
|
+
}
|
|
384449
|
+
/** @private @author ddj 2026年10月29号 @param ctx Lifecycle owner. @param sessions Public Controller. @param id Fixed target. @param text Task body. @returns Admission evidence without switching transport. @sideEffects Retains and releases exactly one generation. */
|
|
384434
384450
|
async function sendOwned(ctx, sessions, id, text) {
|
|
384435
384451
|
const controller = new AbortController();
|
|
384436
384452
|
const reference = sessions.retain(id, {
|
|
@@ -384438,25 +384454,104 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
384438
384454
|
signal: controller.signal
|
|
384439
384455
|
});
|
|
384440
384456
|
let released = false;
|
|
384441
|
-
|
|
384457
|
+
let cleanupFailed = false;
|
|
384458
|
+
/** @private @author ddj 2026年10月29号 Cancels and releases this exact generation once; remembers unload cleanup failures. */
|
|
384442
384459
|
function release() {
|
|
384443
384460
|
if (released) return;
|
|
384444
384461
|
released = true;
|
|
384445
384462
|
controller.abort();
|
|
384446
|
-
|
|
384463
|
+
try {
|
|
384464
|
+
reference.release();
|
|
384465
|
+
} catch (error) {
|
|
384466
|
+
cleanupFailed = true;
|
|
384467
|
+
throw error;
|
|
384468
|
+
}
|
|
384447
384469
|
}
|
|
384448
384470
|
let remove;
|
|
384471
|
+
let started = false;
|
|
384472
|
+
let result = {
|
|
384473
|
+
status: "unavailable",
|
|
384474
|
+
legacyOk: false
|
|
384475
|
+
};
|
|
384449
384476
|
try {
|
|
384450
384477
|
remove = ctx.effect?.(() => release, "vscode-mode: task session reference");
|
|
384451
384478
|
const binding = await reference.ready;
|
|
384452
|
-
if (controller.signal.aborted)
|
|
384453
|
-
|
|
384454
|
-
|
|
384455
|
-
|
|
384456
|
-
|
|
384457
|
-
|
|
384479
|
+
if (controller.signal.aborted) result = {
|
|
384480
|
+
status: "cancelled",
|
|
384481
|
+
legacyOk: false
|
|
384482
|
+
};
|
|
384483
|
+
else {
|
|
384484
|
+
started = true;
|
|
384485
|
+
result = taskReceipt(await binding.session.prompt([{
|
|
384486
|
+
type: "text",
|
|
384487
|
+
text
|
|
384488
|
+
}], "queue", controller.signal));
|
|
384489
|
+
}
|
|
384490
|
+
} catch {
|
|
384491
|
+
result = {
|
|
384492
|
+
status: started ? "unknown" : "unavailable",
|
|
384493
|
+
legacyOk: false
|
|
384494
|
+
};
|
|
384495
|
+
}
|
|
384496
|
+
const cancelled = controller.signal.aborted;
|
|
384497
|
+
try {
|
|
384458
384498
|
release();
|
|
384499
|
+
} catch {
|
|
384500
|
+
cleanupFailed = true;
|
|
384501
|
+
}
|
|
384502
|
+
try {
|
|
384459
384503
|
remove?.();
|
|
384504
|
+
} catch {
|
|
384505
|
+
cleanupFailed = true;
|
|
384506
|
+
}
|
|
384507
|
+
if (cleanupFailed) result = {
|
|
384508
|
+
status: started ? "unknown" : "unavailable",
|
|
384509
|
+
legacyOk: false
|
|
384510
|
+
};
|
|
384511
|
+
if (cancelled) result = {
|
|
384512
|
+
status: "cancelled",
|
|
384513
|
+
legacyOk: result.legacyOk
|
|
384514
|
+
};
|
|
384515
|
+
return result;
|
|
384516
|
+
}
|
|
384517
|
+
/** @private @author ddj 2026年10月29号 @param ctx Service owner. @param id Fixed target. @param text Task body. @returns One legacy-channel attempt; no cross-channel retry. */
|
|
384518
|
+
async function sendLegacy(ctx, id, text) {
|
|
384519
|
+
const input = inputFor(ctx, id);
|
|
384520
|
+
if (typeof input?.setDraft === "function" && typeof input.submit === "function") {
|
|
384521
|
+
let submitted = false;
|
|
384522
|
+
try {
|
|
384523
|
+
input.setDraft(text);
|
|
384524
|
+
submitted = true;
|
|
384525
|
+
input.submit();
|
|
384526
|
+
return {
|
|
384527
|
+
status: "submitted",
|
|
384528
|
+
legacyOk: true
|
|
384529
|
+
};
|
|
384530
|
+
} catch {
|
|
384531
|
+
return {
|
|
384532
|
+
status: submitted ? "unknown" : "unavailable",
|
|
384533
|
+
legacyOk: false
|
|
384534
|
+
};
|
|
384535
|
+
}
|
|
384536
|
+
}
|
|
384537
|
+
const remote = ctx.get("remote.session");
|
|
384538
|
+
if (typeof remote?.prompt !== "function") return httpTaskResult(id, text);
|
|
384539
|
+
try {
|
|
384540
|
+
const payload = {
|
|
384541
|
+
sessionId: id,
|
|
384542
|
+
mode: "queue",
|
|
384543
|
+
content: [{
|
|
384544
|
+
type: "text",
|
|
384545
|
+
text
|
|
384546
|
+
}]
|
|
384547
|
+
};
|
|
384548
|
+
const receipt = await remote.prompt(slotArg(payload));
|
|
384549
|
+
return taskReceipt(receipt?.result ?? receipt);
|
|
384550
|
+
} catch {
|
|
384551
|
+
return {
|
|
384552
|
+
status: "unknown",
|
|
384553
|
+
legacyOk: false
|
|
384554
|
+
};
|
|
384460
384555
|
}
|
|
384461
384556
|
}
|
|
384462
384557
|
/** apiproxy wire 端点(POST /api/<method>;浏览器同源 fetch 自带 cookie 鉴权;实证 401=未鉴权而非 404)。 */
|
|
@@ -384522,16 +384617,8 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
384522
384617
|
const id = json?.result?.value?.sessionId;
|
|
384523
384618
|
return json?.result?.ok === true && typeof id === "string" ? id : "";
|
|
384524
384619
|
}
|
|
384525
|
-
/**
|
|
384526
|
-
|
|
384527
|
-
* 契约 = 当前安装树 SessionsApi.prompt:`{sessionId, mode:'queue', content:[{type:'text',text}]}`。
|
|
384528
|
-
* 发送成功会话即有内容 → 不再被 blank 隐藏(列表可见)。
|
|
384529
|
-
* @author ddj 2026年09月23号
|
|
384530
|
-
* @param sessionId 目标会话 id(新会话)
|
|
384531
|
-
* @param text 任务文本
|
|
384532
|
-
* @returns 是否受理
|
|
384533
|
-
*/
|
|
384534
|
-
async function apiSessionPrompt(sessionId, text) {
|
|
384620
|
+
/** @private @author ddj 2026年10月29号 @param sessionId Fixed target. @param text Task body. @returns HTTP receipt evidence; errors after request entry are never safe rejection. */
|
|
384621
|
+
async function httpTaskResult(sessionId, text) {
|
|
384535
384622
|
try {
|
|
384536
384623
|
const payload = {
|
|
384537
384624
|
sessionId,
|
|
@@ -384541,7 +384628,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
384541
384628
|
text
|
|
384542
384629
|
}]
|
|
384543
384630
|
};
|
|
384544
|
-
return (await (await fetch(appUrl(SESSION_PROMPT_PATH), {
|
|
384631
|
+
return taskReceipt((await (await fetch(appUrl(SESSION_PROMPT_PATH), {
|
|
384545
384632
|
method: "POST",
|
|
384546
384633
|
headers: { "content-type": "application/json" },
|
|
384547
384634
|
body: JSON.stringify({
|
|
@@ -384550,9 +384637,12 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
384550
384637
|
method: "session/prompt",
|
|
384551
384638
|
payload: slotArg(payload)
|
|
384552
384639
|
})
|
|
384553
|
-
})).json().catch(() => null))?.result
|
|
384640
|
+
})).json().catch(() => null))?.result);
|
|
384554
384641
|
} catch {
|
|
384555
|
-
return
|
|
384642
|
+
return {
|
|
384643
|
+
status: "unknown",
|
|
384644
|
+
legacyOk: false
|
|
384645
|
+
};
|
|
384556
384646
|
}
|
|
384557
384647
|
}
|
|
384558
384648
|
/**
|
|
@@ -384755,44 +384845,35 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
384755
384845
|
/**
|
|
384756
384846
|
* 发送任务给目标会话 agent(点击即发送方案:任务落地会话即非 blank,列表可见)。
|
|
384757
384847
|
* 新版持有目标 Controller 到 prompt 结束;旧版按能力选择唯一通道,不跨通道重发。
|
|
384758
|
-
* @author ddj 2026年
|
|
384848
|
+
* @author ddj 2026年10月29号
|
|
384759
384849
|
* @param sessionId 目标会话 id
|
|
384760
384850
|
* @param text 任务文本
|
|
384761
|
-
* @returns
|
|
384851
|
+
* @returns 同一次调用的历史boolean语义,不能作为受理事实
|
|
384762
384852
|
*/
|
|
384763
|
-
const sendTask = async (sessionId, text) =>
|
|
384764
|
-
|
|
384853
|
+
const sendTask = async (sessionId, text) => (await sendTaskResult(sessionId, text)).legacyOk;
|
|
384854
|
+
/** @private @author ddj 2026年10月29号 @param sessionId Fixed target. @param text Task body. @returns Detailed evidence from exactly one selected transport. */
|
|
384855
|
+
async function sendTaskResult(sessionId, text) {
|
|
384856
|
+
if (!sessionId) return {
|
|
384857
|
+
status: "unavailable",
|
|
384858
|
+
legacyOk: false
|
|
384859
|
+
};
|
|
384765
384860
|
try {
|
|
384766
384861
|
const sessions = ctx.get("sessions");
|
|
384767
384862
|
if (typeof sessions?.retain === "function") return await sendOwned(ctx, sessions, sessionId, text);
|
|
384768
|
-
|
|
384769
|
-
if (typeof input?.setDraft === "function" && typeof input.submit === "function") {
|
|
384770
|
-
input.setDraft(text);
|
|
384771
|
-
input.submit();
|
|
384772
|
-
return true;
|
|
384773
|
-
}
|
|
384774
|
-
const remote = ctx.get("remote.session");
|
|
384775
|
-
if (typeof remote?.prompt === "function") {
|
|
384776
|
-
const payload = {
|
|
384777
|
-
sessionId,
|
|
384778
|
-
mode: "queue",
|
|
384779
|
-
content: [{
|
|
384780
|
-
type: "text",
|
|
384781
|
-
text
|
|
384782
|
-
}]
|
|
384783
|
-
};
|
|
384784
|
-
return rpcOk(await remote.prompt(slotArg(payload))).ok;
|
|
384785
|
-
}
|
|
384786
|
-
return await apiSessionPrompt(sessionId, text);
|
|
384863
|
+
return await sendLegacy(ctx, sessionId, text);
|
|
384787
384864
|
} catch {
|
|
384788
|
-
return
|
|
384865
|
+
return {
|
|
384866
|
+
status: "unavailable",
|
|
384867
|
+
legacyOk: false
|
|
384868
|
+
};
|
|
384789
384869
|
}
|
|
384790
|
-
}
|
|
384870
|
+
}
|
|
384791
384871
|
return {
|
|
384792
384872
|
appendReference,
|
|
384793
384873
|
appendText,
|
|
384794
384874
|
startDraftSession,
|
|
384795
|
-
sendTask
|
|
384875
|
+
sendTask,
|
|
384876
|
+
sendTaskResult
|
|
384796
384877
|
};
|
|
384797
384878
|
}
|
|
384798
384879
|
/**
|
|
@@ -407159,10 +407240,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
407159
407240
|
}, [sessionId, stopDeepPoll]);
|
|
407160
407241
|
/**
|
|
407161
407242
|
* 深度分析入口(02-deep-session-prompt 修正版):**新建独立会话**承载任务
|
|
407162
|
-
*
|
|
407163
|
-
*
|
|
407164
|
-
*
|
|
407165
|
-
* @author ddj 2026年09月23号
|
|
407243
|
+
* (不污染当前对话),显式受理才等待方案;不明回执不重发或填入另一草稿。
|
|
407244
|
+
* 未发送或明确拒绝时仅填当前草稿,提示用户手动发送,prompt使用实际草稿会话。
|
|
407245
|
+
* @author ddj 2026年10月29号
|
|
407166
407246
|
*/
|
|
407167
407247
|
const runDeepPlan = react$1.default.useCallback(() => {
|
|
407168
407248
|
const live = ctxRef.current;
|
|
@@ -407178,32 +407258,58 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
407178
407258
|
live?.notify?.("无法填入任务(无会话或输入框不可用)");
|
|
407179
407259
|
return;
|
|
407180
407260
|
}
|
|
407181
|
-
/**
|
|
407182
|
-
|
|
407183
|
-
|
|
407184
|
-
|
|
407185
|
-
live?.notify?.("已填入当前对话(降级),任务已自动发送,请在本对话查看");
|
|
407186
|
-
startDeepPoll(since);
|
|
407187
|
-
} else live?.notify?.("发起失败:" + (outcome === "unavailable" ? "无会话或输入框不可用" : "输入框忙,请重试"));
|
|
407188
|
-
});
|
|
407189
|
-
};
|
|
407190
|
-
/** 统一收尾(点击即发送方案):create 后构建 prompt(内嵌新会话 id)→ 发送任务。 */
|
|
407191
|
-
const sendAndWatch = (id) => {
|
|
407192
|
-
const prompt = buildAgentPrompt(wcRoot, id || sessionId || "");
|
|
407193
|
-
if (!id || typeof add.sendTask !== "function") {
|
|
407194
|
-
degrade(prompt);
|
|
407261
|
+
/** @private @author ddj 2026年10月29号 @returns Draft insertion only; never submits or starts a task poll. */
|
|
407262
|
+
async function draftCurrent() {
|
|
407263
|
+
if (!sessionId) {
|
|
407264
|
+
live?.notify?.("无法填入任务(无会话或输入框不可用)");
|
|
407195
407265
|
return;
|
|
407196
407266
|
}
|
|
407197
|
-
|
|
407198
|
-
|
|
407199
|
-
|
|
407200
|
-
|
|
407201
|
-
|
|
407202
|
-
|
|
407203
|
-
|
|
407204
|
-
|
|
407205
|
-
|
|
407206
|
-
|
|
407267
|
+
try {
|
|
407268
|
+
const outcome = await add.appendText(sessionId, buildAgentPrompt(wcRoot, sessionId));
|
|
407269
|
+
if (outcome === "ok" || outcome === "busy") live?.notify?.("已填入当前草稿,请手动发送");
|
|
407270
|
+
else live?.notify?.("发起失败:" + (outcome === "unavailable" ? "无会话或输入框不可用" : "输入框忙,请重试"));
|
|
407271
|
+
} catch {
|
|
407272
|
+
live?.notify?.("草稿填入失败,请检查当前输入框");
|
|
407273
|
+
}
|
|
407274
|
+
}
|
|
407275
|
+
/** @private @author ddj 2026年10月29号 @param id Created target. @returns One detailed send decision; ambiguous receipts never retry. */
|
|
407276
|
+
async function sendAndWatch(id) {
|
|
407277
|
+
if (!id) {
|
|
407278
|
+
await draftCurrent();
|
|
407279
|
+
return;
|
|
407280
|
+
}
|
|
407281
|
+
if (typeof add.sendTaskResult !== "function") {
|
|
407282
|
+
live?.notify?.("发送接口不支持受理确认,本次未自动发送,请手动处理");
|
|
407283
|
+
return;
|
|
407284
|
+
}
|
|
407285
|
+
let result;
|
|
407286
|
+
try {
|
|
407287
|
+
result = await add.sendTaskResult(id, buildAgentPrompt(wcRoot, id));
|
|
407288
|
+
} catch {
|
|
407289
|
+
result = {
|
|
407290
|
+
status: "unknown",
|
|
407291
|
+
legacyOk: false
|
|
407292
|
+
};
|
|
407293
|
+
}
|
|
407294
|
+
if (result?.status === "accepted") {
|
|
407295
|
+
live?.notify?.("已在新会话「AI 深度分析」受理任务,请在左侧会话列表点开查看进度");
|
|
407296
|
+
startDeepPoll(since);
|
|
407297
|
+
} else if (result?.status === "rejected" || result?.status === "unavailable") await draftCurrent();
|
|
407298
|
+
else if (result?.status === "cancelled") live?.notify?.("发送操作已取消,任务可能已提交,请检查目标会话:" + id);
|
|
407299
|
+
else if (result?.status === "submitted") live?.notify?.("已调用提交但受理未确认,请检查目标会话,不要重复发送:" + id);
|
|
407300
|
+
else live?.notify?.("任务受理未确认,请检查目标会话,不要重复发送:" + id);
|
|
407301
|
+
}
|
|
407302
|
+
/** @private @author ddj 2026年10月29号 @returns Creates a target before sending; creation failure can only insert a manual draft. */
|
|
407303
|
+
async function beginDeep() {
|
|
407304
|
+
let result;
|
|
407305
|
+
try {
|
|
407306
|
+
result = await add.startDraftSession?.(wcRoot || void 0);
|
|
407307
|
+
} catch {
|
|
407308
|
+
result = { ok: false };
|
|
407309
|
+
}
|
|
407310
|
+
await sendAndWatch(result?.ok ? result.id : void 0);
|
|
407311
|
+
}
|
|
407312
|
+
beginDeep();
|
|
407207
407313
|
}, [
|
|
407208
407314
|
aiBusy,
|
|
407209
407315
|
aiRun,
|
package/lib/client.js.map
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/lib/client.js.map
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 35448554 bytes
|
|
4
|
+
sha256: 914a19e1a5249e6bb325b3146b52eb17e2f3687c19a1bea36dd0843e72be1774
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vscode-mode",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.4",
|
|
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 扩展市场安装),状态持久化到工作区旁车,界面状态(页签/光标/侧栏/编辑 Tab/导航历史)镜像到 DSH home 跨重启恢复",
|
|
5
5
|
"icon": "assets/icon.svg",
|
|
6
6
|
"keywords": [
|
|
@@ -281,28 +281,78 @@ interface SessionBridge {
|
|
|
281
281
|
scope?: (id: string) => unknown
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
/**
|
|
285
|
-
|
|
284
|
+
/** Detailed admission evidence; none of these states proves task completion. */
|
|
285
|
+
export interface TaskSendResult {
|
|
286
|
+
status: 'accepted' | 'rejected' | 'unavailable' | 'unknown' | 'submitted' | 'cancelled'
|
|
287
|
+
/** Historical boolean from this same attempt, not an admission marker. */
|
|
288
|
+
legacyOk: boolean
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** @private @author ddj 2026年10月29号 @param receipt Raw selected-channel receipt. @returns Explicit admission evidence and the historical boolean. */
|
|
292
|
+
function taskReceipt(receipt: unknown): TaskSendResult {
|
|
293
|
+
const ok = (receipt as { ok?: unknown } | null)?.ok
|
|
294
|
+
if (ok === true) return { status: 'accepted', legacyOk: true }
|
|
295
|
+
if (ok === false) return { status: 'rejected', legacyOk: false }
|
|
296
|
+
return { status: 'unknown', legacyOk: false }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** @private @author ddj 2026年10月29号 @param ctx Lifecycle owner. @param sessions Public Controller. @param id Fixed target. @param text Task body. @returns Admission evidence without switching transport. @sideEffects Retains and releases exactly one generation. */
|
|
300
|
+
async function sendOwned(ctx: CtxLike, sessions: SessionBridge, id: string, text: string): Promise<TaskSendResult> {
|
|
286
301
|
const controller = new AbortController()
|
|
287
302
|
const reference = sessions.retain!(id, { source: 'controllerOperation', signal: controller.signal })
|
|
288
303
|
let released = false
|
|
289
|
-
|
|
304
|
+
let cleanupFailed = false
|
|
305
|
+
/** @private @author ddj 2026年10月29号 Cancels and releases this exact generation once; remembers unload cleanup failures. */
|
|
290
306
|
function release(): void {
|
|
291
307
|
if (released) return
|
|
292
308
|
released = true
|
|
293
309
|
controller.abort()
|
|
294
|
-
reference.release()
|
|
310
|
+
try { reference.release() } catch (error) { cleanupFailed = true; throw error }
|
|
295
311
|
}
|
|
296
312
|
let remove: (() => void) | undefined
|
|
313
|
+
let started = false
|
|
314
|
+
let result: TaskSendResult = { status: 'unavailable', legacyOk: false }
|
|
297
315
|
try {
|
|
298
316
|
remove = ctx.effect?.(() => release, 'vscode-mode: task session reference')
|
|
299
317
|
const binding = await reference.ready
|
|
300
|
-
if (controller.signal.aborted)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
318
|
+
if (controller.signal.aborted) result = { status: 'cancelled', legacyOk: false }
|
|
319
|
+
else {
|
|
320
|
+
started = true
|
|
321
|
+
result = taskReceipt(await binding.session.prompt([{ type: 'text', text }], 'queue', controller.signal))
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
result = { status: started ? 'unknown' : 'unavailable', legacyOk: false }
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Snapshot cancellation before normal cleanup aborts our own signal.
|
|
328
|
+
const cancelled = controller.signal.aborted
|
|
329
|
+
try { release() } catch { cleanupFailed = true }
|
|
330
|
+
try { remove?.() } catch { cleanupFailed = true }
|
|
331
|
+
if (cleanupFailed) result = { status: started ? 'unknown' : 'unavailable', legacyOk: false }
|
|
332
|
+
if (cancelled) result = { status: 'cancelled', legacyOk: result.legacyOk }
|
|
333
|
+
return result
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** @private @author ddj 2026年10月29号 @param ctx Service owner. @param id Fixed target. @param text Task body. @returns One legacy-channel attempt; no cross-channel retry. */
|
|
337
|
+
async function sendLegacy(ctx: CtxLike, id: string, text: string): Promise<TaskSendResult> {
|
|
338
|
+
const input = inputFor(ctx, id) as { setDraft?: (task: string) => void; submit?: () => void } | undefined
|
|
339
|
+
if (typeof input?.setDraft === 'function' && typeof input.submit === 'function') {
|
|
340
|
+
let submitted = false
|
|
341
|
+
try {
|
|
342
|
+
input.setDraft(text)
|
|
343
|
+
submitted = true
|
|
344
|
+
input.submit()
|
|
345
|
+
return { status: 'submitted', legacyOk: true }
|
|
346
|
+
} catch { return { status: submitted ? 'unknown' : 'unavailable', legacyOk: false } }
|
|
305
347
|
}
|
|
348
|
+
|
|
349
|
+
const remote = ctx.get('remote.session') as { prompt?: (req: unknown) => Promise<unknown> } | undefined
|
|
350
|
+
if (typeof remote?.prompt !== 'function') return httpTaskResult(id, text)
|
|
351
|
+
try {
|
|
352
|
+
const payload = { sessionId: id, mode: 'queue', content: [{ type: 'text', text }] }
|
|
353
|
+
const receipt = await remote.prompt(slotArg(payload))
|
|
354
|
+
return taskReceipt((receipt as { result?: unknown } | null)?.result ?? receipt)
|
|
355
|
+
} catch { return { status: 'unknown', legacyOk: false } }
|
|
306
356
|
}
|
|
307
357
|
|
|
308
358
|
/** 新建会话结果(ok=false 供调用方降级回当前对话注入)。 */
|
|
@@ -386,6 +436,11 @@ async function apiSessionCreate(cwd: string | undefined): Promise<string> {
|
|
|
386
436
|
* @returns 是否受理
|
|
387
437
|
*/
|
|
388
438
|
export async function apiSessionPrompt(sessionId: string, text: string): Promise<boolean> {
|
|
439
|
+
return (await httpTaskResult(sessionId, text)).legacyOk
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** @private @author ddj 2026年10月29号 @param sessionId Fixed target. @param text Task body. @returns HTTP receipt evidence; errors after request entry are never safe rejection. */
|
|
443
|
+
async function httpTaskResult(sessionId: string, text: string): Promise<TaskSendResult> {
|
|
389
444
|
try {
|
|
390
445
|
const payload = { sessionId, mode: 'queue', content: [{ type: 'text', text }] }
|
|
391
446
|
const res = await fetch(appUrl(SESSION_PROMPT_PATH), {
|
|
@@ -399,9 +454,9 @@ export async function apiSessionPrompt(sessionId: string, text: string): Promise
|
|
|
399
454
|
}),
|
|
400
455
|
})
|
|
401
456
|
const json = await res.json().catch(() => null) as { result?: { ok?: boolean } } | null
|
|
402
|
-
return json?.result
|
|
457
|
+
return taskReceipt(json?.result)
|
|
403
458
|
} catch {
|
|
404
|
-
return false
|
|
459
|
+
return { status: 'unknown', legacyOk: false }
|
|
405
460
|
}
|
|
406
461
|
}
|
|
407
462
|
|
|
@@ -466,8 +521,10 @@ export interface AddToConversation {
|
|
|
466
521
|
appendText(sessionId: string | undefined, text: string): Promise<AddOutcome>
|
|
467
522
|
/** 新建独立会话并切换(旁路任务隔离;失败 ok:false 供降级回当前对话)。 */
|
|
468
523
|
startDraftSession(cwd?: string): Promise<DraftSessionResult>
|
|
469
|
-
/**
|
|
524
|
+
/** Historical compatibility result, not proof that a task was accepted. */
|
|
470
525
|
sendTask(sessionId: string | undefined, text: string): Promise<boolean>
|
|
526
|
+
/** Optional on older injected facades; detailed callers must not infer acceptance from sendTask. */
|
|
527
|
+
sendTaskResult?(sessionId: string | undefined, text: string): Promise<TaskSendResult>
|
|
471
528
|
}
|
|
472
529
|
|
|
473
530
|
/**
|
|
@@ -640,32 +697,22 @@ export function createAddToConversation(ctx: CtxLike): AddToConversation {
|
|
|
640
697
|
/**
|
|
641
698
|
* 发送任务给目标会话 agent(点击即发送方案:任务落地会话即非 blank,列表可见)。
|
|
642
699
|
* 新版持有目标 Controller 到 prompt 结束;旧版按能力选择唯一通道,不跨通道重发。
|
|
643
|
-
* @author ddj 2026年
|
|
700
|
+
* @author ddj 2026年10月29号
|
|
644
701
|
* @param sessionId 目标会话 id
|
|
645
702
|
* @param text 任务文本
|
|
646
|
-
* @returns
|
|
703
|
+
* @returns 同一次调用的历史boolean语义,不能作为受理事实
|
|
647
704
|
*/
|
|
648
|
-
const sendTask: AddToConversation['sendTask'] = async (sessionId, text) =>
|
|
649
|
-
|
|
705
|
+
const sendTask: AddToConversation['sendTask'] = async (sessionId, text) => (await sendTaskResult(sessionId, text)).legacyOk
|
|
706
|
+
|
|
707
|
+
/** @private @author ddj 2026年10月29号 @param sessionId Fixed target. @param text Task body. @returns Detailed evidence from exactly one selected transport. */
|
|
708
|
+
async function sendTaskResult(sessionId: string | undefined, text: string): Promise<TaskSendResult> {
|
|
709
|
+
if (!sessionId) return { status: 'unavailable', legacyOk: false }
|
|
650
710
|
try {
|
|
651
711
|
const sessions = ctx.get('sessions') as SessionBridge | undefined
|
|
652
712
|
if (typeof sessions?.retain === 'function') return await sendOwned(ctx, sessions, sessionId, text)
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const input = inputFor(ctx, sessionId) as { setDraft?: (task: string) => void; submit?: () => void } | undefined
|
|
656
|
-
if (typeof input?.setDraft === 'function' && typeof input.submit === 'function') {
|
|
657
|
-
input.setDraft(text)
|
|
658
|
-
input.submit()
|
|
659
|
-
return true
|
|
660
|
-
}
|
|
661
|
-
const remote = ctx.get('remote.session') as { prompt?: (req: unknown) => Promise<unknown> } | undefined
|
|
662
|
-
if (typeof remote?.prompt === 'function') {
|
|
663
|
-
const payload = { sessionId, mode: 'queue', content: [{ type: 'text', text }] }
|
|
664
|
-
return rpcOk(await remote.prompt(slotArg(payload))).ok
|
|
665
|
-
}
|
|
666
|
-
return await apiSessionPrompt(sessionId, text)
|
|
667
|
-
} catch { return false }
|
|
713
|
+
return await sendLegacy(ctx, sessionId, text)
|
|
714
|
+
} catch { return { status: 'unavailable', legacyOk: false } }
|
|
668
715
|
}
|
|
669
716
|
|
|
670
|
-
return { appendReference, appendText, startDraftSession, sendTask }
|
|
717
|
+
return { appendReference, appendText, startDraftSession, sendTask, sendTaskResult }
|
|
671
718
|
}
|
|
@@ -39,6 +39,7 @@ import type { ContextMenuEntry } from '../../ui/ContextMenu.js'
|
|
|
39
39
|
import { SvnAiPlanDialog } from '../../ui/SvnAiPlanDialog.js'
|
|
40
40
|
import { budgetGroupsOf } from './svnListBudget.js'
|
|
41
41
|
import type { SidebarCtx } from '../types.js'
|
|
42
|
+
import type { DraftSessionResult, TaskSendResult } from '../../addToConversation.js'
|
|
42
43
|
|
|
43
44
|
/** 未分组条目的分组键(changelist 为空)。 */
|
|
44
45
|
const NO_CHANGELIST = '(未分组)'
|
|
@@ -845,10 +846,9 @@ export function SvnPanel(props) {
|
|
|
845
846
|
|
|
846
847
|
/**
|
|
847
848
|
* 深度分析入口(02-deep-session-prompt 修正版):**新建独立会话**承载任务
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
* @author ddj 2026年09月23号
|
|
849
|
+
* (不污染当前对话),显式受理才等待方案;不明回执不重发或填入另一草稿。
|
|
850
|
+
* 未发送或明确拒绝时仅填当前草稿,提示用户手动发送,prompt使用实际草稿会话。
|
|
851
|
+
* @author ddj 2026年10月29号
|
|
852
852
|
*/
|
|
853
853
|
const runDeepPlan = React.useCallback(() => {
|
|
854
854
|
const live = ctxRef.current
|
|
@@ -858,34 +858,42 @@ export function SvnPanel(props) {
|
|
|
858
858
|
const since = Date.now()
|
|
859
859
|
const add = live?.addToConversation
|
|
860
860
|
if (!add?.appendText) { live?.notify?.('无法填入任务(无会话或输入框不可用)'); return }
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
})
|
|
861
|
+
|
|
862
|
+
/** @private @author ddj 2026年10月29号 @returns Draft insertion only; never submits or starts a task poll. */
|
|
863
|
+
async function draftCurrent(): Promise<void> {
|
|
864
|
+
if (!sessionId) { live?.notify?.('无法填入任务(无会话或输入框不可用)'); return }
|
|
865
|
+
try {
|
|
866
|
+
const outcome = await add.appendText(sessionId, buildAgentPrompt(wcRoot, sessionId))
|
|
867
|
+
if (outcome === 'ok' || outcome === 'busy') live?.notify?.('已填入当前草稿,请手动发送')
|
|
868
|
+
else live?.notify?.('发起失败:' + (outcome === 'unavailable' ? '无会话或输入框不可用' : '输入框忙,请重试'))
|
|
869
|
+
} catch { live?.notify?.('草稿填入失败,请检查当前输入框') }
|
|
871
870
|
}
|
|
872
|
-
/**
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
if (
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
871
|
+
/** @private @author ddj 2026年10月29号 @param id Created target. @returns One detailed send decision; ambiguous receipts never retry. */
|
|
872
|
+
async function sendAndWatch(id): Promise<void> {
|
|
873
|
+
if (!id) { await draftCurrent(); return }
|
|
874
|
+
if (typeof add.sendTaskResult !== 'function') {
|
|
875
|
+
live?.notify?.('发送接口不支持受理确认,本次未自动发送,请手动处理')
|
|
876
|
+
return
|
|
877
|
+
}
|
|
878
|
+
let result: TaskSendResult | undefined
|
|
879
|
+
try { result = await add.sendTaskResult(id, buildAgentPrompt(wcRoot, id)) }
|
|
880
|
+
catch { result = { status: 'unknown', legacyOk: false } }
|
|
881
|
+
if (result?.status === 'accepted') {
|
|
882
|
+
live?.notify?.('已在新会话「AI 深度分析」受理任务,请在左侧会话列表点开查看进度')
|
|
883
|
+
startDeepPoll(since)
|
|
884
|
+
} else if (result?.status === 'rejected' || result?.status === 'unavailable') await draftCurrent()
|
|
885
|
+
else if (result?.status === 'cancelled') live?.notify?.('发送操作已取消,任务可能已提交,请检查目标会话:' + id)
|
|
886
|
+
else if (result?.status === 'submitted') live?.notify?.('已调用提交但受理未确认,请检查目标会话,不要重复发送:' + id)
|
|
887
|
+
else live?.notify?.('任务受理未确认,请检查目标会话,不要重复发送:' + id)
|
|
888
|
+
}
|
|
889
|
+
/** @private @author ddj 2026年10月29号 @returns Creates a target before sending; creation failure can only insert a manual draft. */
|
|
890
|
+
async function beginDeep(): Promise<void> {
|
|
891
|
+
let result: DraftSessionResult | undefined
|
|
892
|
+
try { result = await add.startDraftSession?.(wcRoot || undefined) }
|
|
893
|
+
catch { result = { ok: false } }
|
|
894
|
+
await sendAndWatch(result?.ok ? result.id : undefined)
|
|
884
895
|
}
|
|
885
|
-
void (
|
|
886
|
-
? add.startDraftSession(wcRoot || undefined)
|
|
887
|
-
: Promise.resolve({ ok: false })
|
|
888
|
-
).then((result) => { sendAndWatch(result?.ok ? result.id : undefined) })
|
|
896
|
+
void beginDeep()
|
|
889
897
|
}, [aiBusy, aiRun, deepWaiting, entries, sessionId, startDeepPoll])
|
|
890
898
|
|
|
891
899
|
// --endregion
|