dsh-vscode-mode 0.26.0 → 0.27.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 +23 -11
- package/lib/client.js +487 -35
- package/lib/client.js.map +2 -2
- package/lib/index.js +398 -183
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/compatSummary.ts +4 -0
- package/src/client/contextMenusOfficial.ts +80 -0
- package/src/client/forgeBridge.ts +28 -0
- package/src/client/index.ts +18 -3
- package/src/client/monaco/lsp/lspClient.ts +25 -7
- package/src/client/officialSidebar.ts +15 -3
- package/src/client/sidebar/menuItems.ts +6 -0
- package/src/client/sidebar/panels/FileExplorer.ts +26 -1
- package/src/client/sidebar/types.ts +3 -0
- package/src/client/state/recordsFeed.ts +40 -3
- package/src/client/state/reviewFeed.ts +9 -2
- package/src/client/tabMenu.ts +5 -0
- package/src/client/ui/ClaimRouter.ts +11 -5
- package/src/client/ui/EditorPane.ts +9 -1
- package/src/client/ui/EditorView.ts +47 -10
- package/src/client/ui/OfficialSideTab.ts +13 -5
- package/src/pending/capture.ts +96 -47
- package/src/pending/store.ts +95 -57
- package/src/pendingCompat.ts +21 -29
- package/src/pendingRpc.ts +5 -6
- package/src/reviewCapture.ts +64 -23
- package/src/reviewEvidence.ts +1 -1
- package/src/reviewRpc.ts +6 -7
- package/src/toolTargets.ts +63 -0
package/lib/index.js
CHANGED
|
@@ -2485,11 +2485,9 @@ const PENDING_PREFIX = "pending:";
|
|
|
2485
2485
|
//#region src/pendingCompat.ts
|
|
2486
2486
|
/**
|
|
2487
2487
|
* 进程内"已被待审接管"的路径集合;旧写入口守卫只读它,绝不碰数据库。
|
|
2488
|
-
*
|
|
2489
|
-
* 无会话归属的登记落在 workspace 集合(兼容只有工作区+路径的旧用法)。
|
|
2488
|
+
* 按工作区登记,来源会话仅作追溯;任一对话重建或注销都会更新同一份集合。
|
|
2490
2489
|
*/
|
|
2491
|
-
const
|
|
2492
|
-
const coveredWorkspace = /* @__PURE__ */ new Set();
|
|
2490
|
+
const coveredWorkspace = /* @__PURE__ */ new Map();
|
|
2493
2491
|
/**
|
|
2494
2492
|
* @author ddj 2026年10月29号
|
|
2495
2493
|
* @description: 由条目 id 派生稳定序号,保证轮询期间面板锚点不跳。
|
|
@@ -2605,16 +2603,18 @@ function coverKey(cwd, path) {
|
|
|
2605
2603
|
return `${pathKey$1(cwd)}|${pathKey$1(path)}`;
|
|
2606
2604
|
}
|
|
2607
2605
|
/**
|
|
2608
|
-
* @
|
|
2609
|
-
* @
|
|
2610
|
-
* @
|
|
2611
|
-
* @
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2606
|
+
* @private
|
|
2607
|
+
* @author ddj 2026年11月04号
|
|
2608
|
+
* @description: 取工作区唯一接管集合;来源对话仅保留为诊断信息。
|
|
2609
|
+
* @param cwd 工作区路径
|
|
2610
|
+
* @returns 路径到登记来源的映射
|
|
2611
|
+
*/
|
|
2612
|
+
function workspaceCovers(cwd) {
|
|
2613
|
+
const workspaceKey = pathKey$1(cwd);
|
|
2614
|
+
const existing = coveredWorkspace.get(workspaceKey);
|
|
2615
2615
|
if (existing) return existing;
|
|
2616
|
-
const created = /* @__PURE__ */ new
|
|
2617
|
-
|
|
2616
|
+
const created = /* @__PURE__ */ new Map();
|
|
2617
|
+
coveredWorkspace.set(workspaceKey, created);
|
|
2618
2618
|
return created;
|
|
2619
2619
|
}
|
|
2620
2620
|
/**
|
|
@@ -2628,27 +2628,27 @@ function sessionCovers(sessionId) {
|
|
|
2628
2628
|
*/
|
|
2629
2629
|
function trackPendingCover(cwd, path, covered, sessionId) {
|
|
2630
2630
|
const key = coverKey(cwd, path);
|
|
2631
|
-
const target =
|
|
2632
|
-
if (covered) target.
|
|
2631
|
+
const target = workspaceCovers(cwd);
|
|
2632
|
+
if (covered) target.set(key, sessionId ?? null);
|
|
2633
2633
|
else target.delete(key);
|
|
2634
2634
|
}
|
|
2635
2635
|
/**
|
|
2636
2636
|
* @author ddj 2026年11月3号
|
|
2637
|
-
* @description:
|
|
2637
|
+
* @description: 用完整列表重建工作区接管集合,清除任何对话留下的过期登记。
|
|
2638
2638
|
* @param cwd 工作区
|
|
2639
2639
|
* @param sessionId 会话 id
|
|
2640
2640
|
* @param paths 本会话当前可见的待审条目路径
|
|
2641
2641
|
* @returns 无
|
|
2642
2642
|
*/
|
|
2643
2643
|
function setPendingCovers(cwd, sessionId, paths) {
|
|
2644
|
-
const next = /* @__PURE__ */ new
|
|
2645
|
-
for (const path of paths) if (path) next.
|
|
2646
|
-
|
|
2644
|
+
const next = /* @__PURE__ */ new Map();
|
|
2645
|
+
for (const path of paths) if (path) next.set(coverKey(cwd, path), sessionId);
|
|
2646
|
+
coveredWorkspace.set(pathKey$1(cwd), next);
|
|
2647
2647
|
}
|
|
2648
2648
|
/**
|
|
2649
2649
|
* @author ddj 2026年10月29号
|
|
2650
2650
|
* @description: 判断某路径是否已由待审投影接管;仅读进程内集合,不产生任何 IO。
|
|
2651
|
-
*
|
|
2651
|
+
* 所有对话按同一工作区集合判定,兼容参数不影响可见范围。
|
|
2652
2652
|
* @param cwd 工作区
|
|
2653
2653
|
* @param path 请求路径
|
|
2654
2654
|
* @param sessionId 会话 id;缺省按并集判定
|
|
@@ -2656,10 +2656,7 @@ function setPendingCovers(cwd, sessionId, paths) {
|
|
|
2656
2656
|
*/
|
|
2657
2657
|
function pendingCovers(cwd, path, sessionId) {
|
|
2658
2658
|
const key = coverKey(cwd, path);
|
|
2659
|
-
|
|
2660
|
-
if (sessionId) return coveredBySession.get(sessionId)?.has(key) === true;
|
|
2661
|
-
for (const set of coveredBySession.values()) if (set.has(key)) return true;
|
|
2662
|
-
return false;
|
|
2659
|
+
return coveredWorkspace.get(pathKey$1(cwd))?.has(key) === true;
|
|
2663
2660
|
}
|
|
2664
2661
|
/**
|
|
2665
2662
|
* @author ddj 2026年10月29号
|
|
@@ -8780,6 +8777,67 @@ function evidenceText(evidence) {
|
|
|
8780
8777
|
};
|
|
8781
8778
|
}
|
|
8782
8779
|
//#endregion
|
|
8780
|
+
//#region src/toolTargets.ts
|
|
8781
|
+
/** Optional Host patch target extraction; results never substitute raw preimages. @author ddj 2026年11月04号 */
|
|
8782
|
+
/** @public @author ddj 2026年11月04号 @param result Untrusted tool envelope. @returns Diagnostic-only status; never writable preimages. */
|
|
8783
|
+
function patchDiagnostics(result) {
|
|
8784
|
+
const payload = result?.value ?? result;
|
|
8785
|
+
if (payload?.phase !== "commit" || !Array.isArray(payload.operations)) return ["apply_patch result unavailable or malformed"];
|
|
8786
|
+
const diagnostics = [];
|
|
8787
|
+
for (const operation of payload.operations) {
|
|
8788
|
+
if (!operation || typeof operation.path !== "string" || ![
|
|
8789
|
+
"add",
|
|
8790
|
+
"update",
|
|
8791
|
+
"delete",
|
|
8792
|
+
"move"
|
|
8793
|
+
].includes(operation.kind) || ![
|
|
8794
|
+
"completed",
|
|
8795
|
+
"failed",
|
|
8796
|
+
"unknown",
|
|
8797
|
+
"skipped"
|
|
8798
|
+
].includes(operation.status)) {
|
|
8799
|
+
diagnostics.push("apply_patch operation malformed");
|
|
8800
|
+
continue;
|
|
8801
|
+
}
|
|
8802
|
+
if (operation.status !== "completed") diagnostics.push(operation.path + ": " + operation.status);
|
|
8803
|
+
if (operation.changes !== void 0 && (!Array.isArray(operation.changes) || operation.changes.some((change) => !change || typeof change.path !== "string" || !(change.before === null || typeof change.before === "string") || !(change.after === null || typeof change.after === "string")))) diagnostics.push(operation.path + ": changes malformed");
|
|
8804
|
+
}
|
|
8805
|
+
return diagnostics;
|
|
8806
|
+
}
|
|
8807
|
+
let parserLoad;
|
|
8808
|
+
/** @public @author ddj 2026年11月04号 @returns Optional official parser; missing capabilities remain retryable. */
|
|
8809
|
+
async function patchParser() {
|
|
8810
|
+
if (!parserLoad) parserLoad = loadHostModule("@deepseek-ai/dsh-fs-patch").then((module) => {
|
|
8811
|
+
const parser = module.parsePatch;
|
|
8812
|
+
return typeof parser === "function" ? parser : null;
|
|
8813
|
+
}).catch(() => null);
|
|
8814
|
+
const parser = await parserLoad;
|
|
8815
|
+
if (!parser) parserLoad = void 0;
|
|
8816
|
+
return parser;
|
|
8817
|
+
}
|
|
8818
|
+
/** @public @author ddj 2026年11月04号 @param call Tool identity and arguments. @param parser Optional injectable official parser. @returns Unique unresolved source/destination paths. @throws Unsupported parser or malformed patch. */
|
|
8819
|
+
async function toolTargets(call, parser) {
|
|
8820
|
+
if ([
|
|
8821
|
+
"edit",
|
|
8822
|
+
"write",
|
|
8823
|
+
"str_replace_editor"
|
|
8824
|
+
].includes(String(call.name))) {
|
|
8825
|
+
const path = call.arguments?.file_path ?? call.arguments?.path;
|
|
8826
|
+
return typeof path === "string" && path ? [path] : [];
|
|
8827
|
+
}
|
|
8828
|
+
if (call.name !== "apply_patch") return [];
|
|
8829
|
+
if (typeof call.arguments?.input !== "string") throw new Error("apply_patch input unavailable");
|
|
8830
|
+
const parse = parser === void 0 ? await patchParser() : parser;
|
|
8831
|
+
if (!parse) throw new Error("Host patch parser unavailable");
|
|
8832
|
+
const paths = /* @__PURE__ */ new Set();
|
|
8833
|
+
for (const operation of parse(call.arguments.input)) {
|
|
8834
|
+
if (typeof operation.path !== "string" || !operation.path) throw new Error("Invalid patch target");
|
|
8835
|
+
paths.add(operation.path);
|
|
8836
|
+
if (operation.moveTo) paths.add(operation.moveTo);
|
|
8837
|
+
}
|
|
8838
|
+
return [...paths];
|
|
8839
|
+
}
|
|
8840
|
+
//#endregion
|
|
8783
8841
|
//#region src/reviewCapture.ts
|
|
8784
8842
|
/** Independent byte evidence; official diff remains the only display authority. @author ddj 2026年10月27号 */
|
|
8785
8843
|
/** @public @author ddj 2026年10月27号 @param ctx Host lifecycle owner. @returns Scoped byte recorder; failures disable undo evidence only, never tool execution. */
|
|
@@ -8787,12 +8845,15 @@ function installEvidence(ctx) {
|
|
|
8787
8845
|
const chains = /* @__PURE__ */ new Map();
|
|
8788
8846
|
const staged = /* @__PURE__ */ new Map();
|
|
8789
8847
|
let stopped = false;
|
|
8848
|
+
const disposed = /* @__PURE__ */ new Set();
|
|
8790
8849
|
ctx.on("tools/execute", aroundTool);
|
|
8791
8850
|
ctx.on("session/event", onEvent);
|
|
8792
8851
|
ctx.on("session/disposed", onDisposed);
|
|
8793
8852
|
/** @private @author ddj 2026年10月27号 @param session Disposed owner. Drops optional in-memory evidence, never persisted official facts. */
|
|
8794
8853
|
function onDisposed(session) {
|
|
8854
|
+
disposed.add(session.id);
|
|
8795
8855
|
chains.delete(session.id);
|
|
8856
|
+
for (const key of staged.keys()) if (JSON.parse(key)[0] === session.id) staged.delete(key);
|
|
8796
8857
|
}
|
|
8797
8858
|
/** @private @author ddj 2026年10月27号 @returns Current bounded capture footprint, excluding transient filesystem reads. */
|
|
8798
8859
|
function footprint() {
|
|
@@ -8803,48 +8864,94 @@ function installEvidence(ctx) {
|
|
|
8803
8864
|
/** @private @author ddj 2026年10月27号 @param exec Allowed tool dispatch. @param next Original dispatch. @returns Original result unchanged, regardless of capture failure. */
|
|
8804
8865
|
async function aroundTool(exec, next) {
|
|
8805
8866
|
const session = exec.agent?.session;
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
let fs;
|
|
8809
|
-
let chain;
|
|
8810
|
-
let target;
|
|
8867
|
+
if (stopped || !session?.id) return next();
|
|
8868
|
+
const captures = [];
|
|
8811
8869
|
try {
|
|
8812
|
-
|
|
8813
|
-
fs = ctx.get("fs");
|
|
8814
|
-
|
|
8815
|
-
const
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
files = /* @__PURE__ */ new Map();
|
|
8820
|
-
chains.set(session.id, files);
|
|
8821
|
-
}
|
|
8822
|
-
if (files.size >= 64 && !files.has(identity)) return next();
|
|
8823
|
-
chain = files.get(identity);
|
|
8824
|
-
if (!chain) {
|
|
8825
|
-
chain = {
|
|
8826
|
-
before,
|
|
8827
|
-
after: before,
|
|
8828
|
-
busy: false,
|
|
8829
|
-
invalid: false
|
|
8830
|
-
};
|
|
8831
|
-
files.set(identity, chain);
|
|
8832
|
-
}
|
|
8833
|
-
if (chain.busy || chain.after.hash !== before.hash) chain.invalid = true;
|
|
8834
|
-
chain.busy = true;
|
|
8870
|
+
const cwd = cwdOfSession(ctx, session);
|
|
8871
|
+
const fs = ctx.get("fs");
|
|
8872
|
+
const paths = await toolTargets(exec);
|
|
8873
|
+
for (const path of paths) try {
|
|
8874
|
+
const capture = await prepareRaw(fs, session.id, cwd ?? void 0, path, exec.signal);
|
|
8875
|
+
if (capture) captures.push(capture);
|
|
8876
|
+
} catch {}
|
|
8835
8877
|
} catch {}
|
|
8836
8878
|
try {
|
|
8837
8879
|
return await next();
|
|
8838
8880
|
} finally {
|
|
8839
|
-
|
|
8881
|
+
for (const capture of captures) {
|
|
8840
8882
|
try {
|
|
8841
|
-
|
|
8883
|
+
const after = await readRaw(capture.fs, capture.target, exec.signal);
|
|
8884
|
+
if (!stopped && !disposed.has(session.id)) {
|
|
8885
|
+
capture.chain.after = after;
|
|
8886
|
+
if (footprint() > 33554432) {
|
|
8887
|
+
capture.chain.invalid = true;
|
|
8888
|
+
capture.chain.before = rawReviewFile(null);
|
|
8889
|
+
capture.chain.after = rawReviewFile(null);
|
|
8890
|
+
}
|
|
8891
|
+
}
|
|
8842
8892
|
} catch {
|
|
8843
|
-
chain.invalid = true;
|
|
8893
|
+
capture.chain.invalid = true;
|
|
8844
8894
|
}
|
|
8895
|
+
capture.chain.busy = false;
|
|
8896
|
+
}
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8899
|
+
/** @private @author ddj 2026年11月04号 @param sessionId Frozen owner. @param cwd Frozen workspace. @param path Patch target. @param signal Tool cancellation. @returns Reserved raw capture or no evidence. */
|
|
8900
|
+
async function prepareRaw(fs, sessionId, cwd, path, signal) {
|
|
8901
|
+
if (stopped || disposed.has(sessionId) || !cwd || chains.size >= 16 && !chains.has(sessionId) || footprint() >= 33554432) return null;
|
|
8902
|
+
const target = await fs.resolve(path, { cwd });
|
|
8903
|
+
if (stopped || disposed.has(sessionId)) return null;
|
|
8904
|
+
const identity = fs.processPath(target);
|
|
8905
|
+
let files = chains.get(sessionId);
|
|
8906
|
+
if (!files) {
|
|
8907
|
+
files = /* @__PURE__ */ new Map();
|
|
8908
|
+
chains.set(sessionId, files);
|
|
8909
|
+
}
|
|
8910
|
+
if (files.size >= 64 && !files.has(identity)) return null;
|
|
8911
|
+
let chain = files.get(identity);
|
|
8912
|
+
if (chain?.busy) {
|
|
8913
|
+
chain.invalid = true;
|
|
8914
|
+
return null;
|
|
8915
|
+
}
|
|
8916
|
+
const empty = rawReviewFile(null);
|
|
8917
|
+
if (!chain) {
|
|
8918
|
+
chain = {
|
|
8919
|
+
before: empty,
|
|
8920
|
+
after: empty,
|
|
8921
|
+
busy: true,
|
|
8922
|
+
invalid: false
|
|
8923
|
+
};
|
|
8924
|
+
files.set(identity, chain);
|
|
8925
|
+
try {
|
|
8926
|
+
chain.before = await readRaw(fs, target, signal);
|
|
8927
|
+
chain.after = chain.before;
|
|
8928
|
+
} catch (error) {
|
|
8929
|
+
chain.invalid = true;
|
|
8845
8930
|
chain.busy = false;
|
|
8931
|
+
throw error;
|
|
8846
8932
|
}
|
|
8933
|
+
} else {
|
|
8934
|
+
chain.busy = true;
|
|
8935
|
+
try {
|
|
8936
|
+
const before = await readRaw(fs, target, signal);
|
|
8937
|
+
if (chain.after.hash !== before.hash) chain.invalid = true;
|
|
8938
|
+
} catch (error) {
|
|
8939
|
+
chain.invalid = true;
|
|
8940
|
+
chain.busy = false;
|
|
8941
|
+
throw error;
|
|
8942
|
+
}
|
|
8943
|
+
}
|
|
8944
|
+
if (stopped || disposed.has(sessionId) || footprint() > 33554432) {
|
|
8945
|
+
chain.invalid = true;
|
|
8946
|
+
chain.busy = false;
|
|
8947
|
+
files.delete(identity);
|
|
8948
|
+
return null;
|
|
8847
8949
|
}
|
|
8950
|
+
return {
|
|
8951
|
+
fs,
|
|
8952
|
+
target,
|
|
8953
|
+
chain
|
|
8954
|
+
};
|
|
8848
8955
|
}
|
|
8849
8956
|
/** @private @author ddj 2026年10月27号 @param session Source session. @param event Published official turn. Freezes the chain before the next turn can mutate it. */
|
|
8850
8957
|
function onEvent(session, event) {
|
|
@@ -8908,10 +9015,34 @@ async function readRaw(fs, target, signal) {
|
|
|
8908
9015
|
const WRITE_TOOLS = [
|
|
8909
9016
|
"edit",
|
|
8910
9017
|
"write",
|
|
8911
|
-
"str_replace_editor"
|
|
9018
|
+
"str_replace_editor",
|
|
9019
|
+
"apply_patch"
|
|
8912
9020
|
];
|
|
8913
9021
|
/** 单文件文本上限:超过即标注 oversized,不静默截断。 */
|
|
8914
9022
|
const MAX_TEXT = 1048576;
|
|
9023
|
+
const captureGaps = /* @__PURE__ */ new Map();
|
|
9024
|
+
/** @public @author ddj 2026年11月04号 @param cwd Workspace identity. @returns Bounded runtime-only capture diagnostics. */
|
|
9025
|
+
function pendingGaps(cwd) {
|
|
9026
|
+
return [...captureGaps.values()].filter((gap) => gap.cwd === cwd).map((gap) => gap.message);
|
|
9027
|
+
}
|
|
9028
|
+
/** @private @author ddj 2026年11月04号 @param ctx Host scope. @param exec Original call. @param path Failed target. @param error Evidence failure. Records no writable state. */
|
|
9029
|
+
function recordGap(scope, exec, path, error) {
|
|
9030
|
+
const { cwd, sessionId } = scope;
|
|
9031
|
+
const key = JSON.stringify([
|
|
9032
|
+
cwd,
|
|
9033
|
+
sessionId,
|
|
9034
|
+
exec.id,
|
|
9035
|
+
path
|
|
9036
|
+
]);
|
|
9037
|
+
captureGaps.set(key, {
|
|
9038
|
+
cwd,
|
|
9039
|
+
sessionId,
|
|
9040
|
+
message: path + ": " + String(error)
|
|
9041
|
+
});
|
|
9042
|
+
const local = [...captureGaps].filter(([, gap]) => gap.cwd === cwd);
|
|
9043
|
+
while (local.length > 50) captureGaps.delete(local.shift()[0]);
|
|
9044
|
+
while (captureGaps.size > 500) captureGaps.delete(captureGaps.keys().next().value);
|
|
9045
|
+
}
|
|
8915
9046
|
/**
|
|
8916
9047
|
* @author ddj 2026年10月30号
|
|
8917
9048
|
* @description: 安装写文件采集;读写失败只影响待审来源,绝不影响用户工具本身。
|
|
@@ -8922,12 +9053,18 @@ const MAX_TEXT = 1048576;
|
|
|
8922
9053
|
function installPending(ctx, sink) {
|
|
8923
9054
|
const state = {
|
|
8924
9055
|
busy: /* @__PURE__ */ new Set(),
|
|
8925
|
-
stopped: false
|
|
9056
|
+
stopped: false,
|
|
9057
|
+
disposed: /* @__PURE__ */ new Set()
|
|
8926
9058
|
};
|
|
8927
9059
|
ctx.on("tools/execute", (exec, next) => aroundTool(ctx, state, sink, exec, next));
|
|
9060
|
+
ctx.on("session/disposed", (session) => {
|
|
9061
|
+
state.disposed.add(session.id);
|
|
9062
|
+
for (const [key, gap] of captureGaps) if (gap.sessionId === session.id) captureGaps.delete(key);
|
|
9063
|
+
});
|
|
8928
9064
|
return { dispose() {
|
|
8929
9065
|
state.stopped = true;
|
|
8930
9066
|
state.busy.clear();
|
|
9067
|
+
captureGaps.clear();
|
|
8931
9068
|
} };
|
|
8932
9069
|
}
|
|
8933
9070
|
/**
|
|
@@ -8938,27 +9075,32 @@ function installPending(ctx, sink) {
|
|
|
8938
9075
|
* @param exec 工具分发上下文
|
|
8939
9076
|
* @returns 采集计划或 null
|
|
8940
9077
|
*/
|
|
8941
|
-
async function planOf(
|
|
8942
|
-
const
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
if (!WRITE_TOOLS.includes(String(exec.name))) return null;
|
|
8946
|
-
const cwd = cwdOf(session);
|
|
8947
|
-
if (!cwd) return null;
|
|
8948
|
-
const fs = ctx.get("fs");
|
|
8949
|
-
const target = await resolveTarget(ctx, session, requested);
|
|
9078
|
+
async function planOf(scope, state, requested) {
|
|
9079
|
+
const { cwd, sessionId, fs } = scope;
|
|
9080
|
+
if (state.stopped || state.disposed.has(sessionId)) return null;
|
|
9081
|
+
const target = await fs.resolve(requested, { cwd });
|
|
8950
9082
|
const identity = fs.processPath(target);
|
|
8951
|
-
if (typeof identity !== "string" || !identity || state.busy.has(identity)) return null;
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
9083
|
+
if (state.stopped || state.disposed.has(sessionId) || typeof identity !== "string" || !identity || state.busy.has(identity)) return null;
|
|
9084
|
+
state.busy.add(identity);
|
|
9085
|
+
try {
|
|
9086
|
+
const before = await readText(fs, target);
|
|
9087
|
+
if (state.stopped || state.disposed.has(sessionId) || before !== null && before.length > MAX_TEXT) {
|
|
9088
|
+
state.busy.delete(identity);
|
|
9089
|
+
return null;
|
|
9090
|
+
}
|
|
9091
|
+
return {
|
|
9092
|
+
cwd: String(cwd),
|
|
9093
|
+
sessionId,
|
|
9094
|
+
path: requested,
|
|
9095
|
+
identity,
|
|
9096
|
+
before,
|
|
9097
|
+
target,
|
|
9098
|
+
fs
|
|
9099
|
+
};
|
|
9100
|
+
} catch (error) {
|
|
9101
|
+
state.busy.delete(identity);
|
|
9102
|
+
throw error;
|
|
9103
|
+
}
|
|
8962
9104
|
}
|
|
8963
9105
|
/**
|
|
8964
9106
|
* @author ddj 2026年10月30号
|
|
@@ -8971,63 +9113,87 @@ async function planOf(ctx, state, exec) {
|
|
|
8971
9113
|
* @returns 原始工具结果
|
|
8972
9114
|
*/
|
|
8973
9115
|
async function aroundTool(ctx, state, sink, exec, next) {
|
|
8974
|
-
|
|
9116
|
+
if (state.stopped || !WRITE_TOOLS.includes(String(exec.name))) return next();
|
|
9117
|
+
const session = exec.agent?.session;
|
|
9118
|
+
const cwd = session ? cwdOfSession(ctx, session) : null;
|
|
9119
|
+
if (!session?.id || !cwd) return next();
|
|
9120
|
+
const scope = {
|
|
9121
|
+
cwd,
|
|
9122
|
+
sessionId: session.id,
|
|
9123
|
+
fs: ctx.get("fs")
|
|
9124
|
+
};
|
|
9125
|
+
const plans = [];
|
|
8975
9126
|
try {
|
|
8976
|
-
|
|
9127
|
+
const paths = await toolTargets(exec);
|
|
9128
|
+
for (const path of paths) try {
|
|
9129
|
+
if (plans.length >= 16) throw new Error("capture target budget exceeded");
|
|
9130
|
+
const plan = await planOf(scope, state, path);
|
|
9131
|
+
if (plan) plans.push(plan);
|
|
9132
|
+
} catch (error) {
|
|
9133
|
+
if (!state.stopped && !state.disposed.has(scope.sessionId)) recordGap(scope, exec, path, error);
|
|
9134
|
+
log.debug("待审采集跳过:" + path + ": " + String(error));
|
|
9135
|
+
}
|
|
8977
9136
|
} catch (error) {
|
|
8978
|
-
|
|
9137
|
+
if (!state.stopped && !state.disposed.has(scope.sessionId)) recordGap(scope, exec, String(exec.name), error);
|
|
9138
|
+
log.debug("待审目标不可用:" + String(error));
|
|
8979
9139
|
}
|
|
8980
|
-
if (!plan) return next();
|
|
8981
|
-
state.busy.add(plan.identity);
|
|
8982
9140
|
try {
|
|
8983
|
-
|
|
9141
|
+
const result = await next();
|
|
9142
|
+
if (exec.name === "apply_patch" && !state.stopped && !state.disposed.has(scope.sessionId)) for (const diagnostic of patchDiagnostics(result)) recordGap(scope, exec, "apply_patch", diagnostic);
|
|
9143
|
+
return result;
|
|
8984
9144
|
} finally {
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
reportWrite(ctx, sink, plan);
|
|
9145
|
+
for (const plan of plans) try {
|
|
9146
|
+
await reportWrite(state, sink, plan);
|
|
8988
9147
|
} catch (error) {
|
|
9148
|
+
if (!state.stopped && !state.disposed.has(scope.sessionId)) recordGap(scope, exec, plan.path, error);
|
|
8989
9149
|
log.debug("待审上报失败:" + String(error));
|
|
9150
|
+
} finally {
|
|
9151
|
+
state.busy.delete(plan.identity);
|
|
8990
9152
|
}
|
|
8991
9153
|
}
|
|
8992
9154
|
}
|
|
8993
9155
|
/**
|
|
8994
|
-
* @author ddj 2026年
|
|
8995
|
-
* @description:
|
|
8996
|
-
* @param
|
|
9156
|
+
* @author ddj 2026年11月04号
|
|
9157
|
+
* @description: 固定目标写后读回;内容一致不产生条目,卸载后不再上报。
|
|
9158
|
+
* @param state 采集生命周期状态
|
|
8997
9159
|
* @param sink 已确认写入的接收方
|
|
8998
9160
|
* @param plan 采集计划
|
|
8999
9161
|
* @returns 无;读回失败只记日志
|
|
9000
9162
|
*/
|
|
9001
|
-
function reportWrite(
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9163
|
+
async function reportWrite(state, sink, plan) {
|
|
9164
|
+
if (state.stopped || state.disposed.has(plan.sessionId)) return;
|
|
9165
|
+
const after = await readText(plan.fs, plan.target);
|
|
9166
|
+
if (state.stopped || state.disposed.has(plan.sessionId)) return;
|
|
9167
|
+
if (after !== null && after.length > MAX_TEXT) throw new Error("capture text budget exceeded");
|
|
9168
|
+
for (const [key, gap] of captureGaps) {
|
|
9169
|
+
const identity = JSON.parse(key);
|
|
9170
|
+
if (gap.cwd === plan.cwd && gap.sessionId === plan.sessionId && identity[3] === plan.path) captureGaps.delete(key);
|
|
9171
|
+
}
|
|
9172
|
+
if (plan.before === after) return;
|
|
9173
|
+
sink({
|
|
9174
|
+
cwd: plan.cwd,
|
|
9175
|
+
sessionId: plan.sessionId,
|
|
9176
|
+
path: plan.path,
|
|
9177
|
+
identity: plan.identity,
|
|
9178
|
+
before: plan.before,
|
|
9179
|
+
after,
|
|
9180
|
+
now: Date.now()
|
|
9181
|
+
});
|
|
9017
9182
|
}
|
|
9018
9183
|
/**
|
|
9019
9184
|
* @author ddj 2026年10月29号
|
|
9020
|
-
* @description:
|
|
9185
|
+
* @description: 读取全文;只有明确缺失返回 null,不可读或超限错误保留失败语义。
|
|
9021
9186
|
* @param fs 文件系统
|
|
9022
9187
|
* @param target 目标
|
|
9023
9188
|
* @returns 文本或 null
|
|
9024
9189
|
*/
|
|
9025
|
-
async function readText(fs, target) {
|
|
9190
|
+
async function readText(fs, target, signal) {
|
|
9026
9191
|
try {
|
|
9027
|
-
return
|
|
9192
|
+
if (typeof fs.readBytes === "function") return rawText(rawReviewFile(await fs.readBytes(target, signal, MAX_TEXT)));
|
|
9193
|
+
throw new Error("Raw byte capture unavailable");
|
|
9028
9194
|
} catch (error) {
|
|
9029
9195
|
const code = error.code;
|
|
9030
|
-
if (code === "ENOENT" || code === "FS_NOT_FOUND"
|
|
9196
|
+
if (code === "ENOENT" || code === "FS_NOT_FOUND") return null;
|
|
9031
9197
|
throw error;
|
|
9032
9198
|
}
|
|
9033
9199
|
}
|
|
@@ -10155,18 +10321,39 @@ function itemOf(stored, baselineEmpty = false) {
|
|
|
10155
10321
|
return item;
|
|
10156
10322
|
}
|
|
10157
10323
|
/**
|
|
10324
|
+
* @private
|
|
10325
|
+
* @author ddj 2026年11月04号
|
|
10326
|
+
* @description 从条目来源判断创建基线;任何决定历史都阻止展示层复活已推进的旧基线。
|
|
10327
|
+
* @param cwd 工作区
|
|
10328
|
+
* @param stored 累计条目
|
|
10329
|
+
* @param sourceAdds 单次读取内的来源创建集合缓存
|
|
10330
|
+
* @returns 是否将展示基线置空
|
|
10331
|
+
*/
|
|
10332
|
+
function emptyBaseOf(cwd, stored, sourceAdds = /* @__PURE__ */ new Map()) {
|
|
10333
|
+
if (isSettled(stored.entry)) return false;
|
|
10334
|
+
if (pendingDb(cwd).prepare("SELECT 1 FROM pending_history WHERE entry_id=? LIMIT 1").get(stored.entry.id)) return false;
|
|
10335
|
+
for (const sourceId of stored.entry.sessionIds) {
|
|
10336
|
+
let createdPaths = sourceAdds.get(sourceId);
|
|
10337
|
+
if (!createdPaths) {
|
|
10338
|
+
createdPaths = addedFiles(cwd, sourceId);
|
|
10339
|
+
sourceAdds.set(sourceId, createdPaths);
|
|
10340
|
+
}
|
|
10341
|
+
if (createdPaths.has(stored.entry.id)) return true;
|
|
10342
|
+
}
|
|
10343
|
+
return false;
|
|
10344
|
+
}
|
|
10345
|
+
/**
|
|
10158
10346
|
* @author ddj 2026年10月29号
|
|
10159
|
-
* @description:
|
|
10347
|
+
* @description: 读工作区全部待审条目,按更新时间升序;来源会话仅用于追溯。
|
|
10160
10348
|
* @param cwd 工作区
|
|
10161
|
-
* @param
|
|
10162
|
-
* @param wholeAdd 本次会话新建的文件身份集合,命中时基线置空
|
|
10349
|
+
* @param _sessionId 兼容旧调用签名,不再用于可见性过滤
|
|
10163
10350
|
* @returns 列表
|
|
10164
10351
|
*/
|
|
10165
|
-
function loadPage(cwd,
|
|
10166
|
-
const
|
|
10167
|
-
const
|
|
10168
|
-
const items = visible.map((stored) => itemOf(stored,
|
|
10169
|
-
const gaps =
|
|
10352
|
+
function loadPage(cwd, _sessionId) {
|
|
10353
|
+
const visible = loadEntries(cwd);
|
|
10354
|
+
const sourceAdds = /* @__PURE__ */ new Map();
|
|
10355
|
+
const items = visible.map((stored) => itemOf(stored, emptyBaseOf(cwd, stored, sourceAdds)));
|
|
10356
|
+
const gaps = pendingGaps(cwd);
|
|
10170
10357
|
for (const stored of visible) if (stored.flags.oversized) gaps.push(`${stored.entry.path}: 文本超出上限,仅保留标记`);
|
|
10171
10358
|
return {
|
|
10172
10359
|
items,
|
|
@@ -10186,9 +10373,10 @@ function loadPage(cwd, sessionId, wholeAdd) {
|
|
|
10186
10373
|
function loadDetail(cwd, id, baselineEmpty = false) {
|
|
10187
10374
|
const stored = loadEntries(cwd).find((item) => item.entry.id === id);
|
|
10188
10375
|
if (!stored) return null;
|
|
10189
|
-
const
|
|
10376
|
+
const emptyBase = baselineEmpty || emptyBaseOf(cwd, stored);
|
|
10377
|
+
const diff = diffOf(stored.entry, emptyBase);
|
|
10190
10378
|
return {
|
|
10191
|
-
item: itemOf(stored,
|
|
10379
|
+
item: itemOf(stored, emptyBase),
|
|
10192
10380
|
diff,
|
|
10193
10381
|
blocks: blockStatesOf(stored.entry),
|
|
10194
10382
|
capability: capabilityOf(stored.entry, stored.flags)
|
|
@@ -10288,23 +10476,25 @@ function writerFor(fs, cwd, identity, expected, signal, policy, allowCreate = fa
|
|
|
10288
10476
|
reason: "resolve-failed"
|
|
10289
10477
|
};
|
|
10290
10478
|
}
|
|
10291
|
-
const
|
|
10292
|
-
if (
|
|
10479
|
+
const snapshot = await stableRead(fs, target, signal);
|
|
10480
|
+
if (!snapshot.ok) return snapshot;
|
|
10481
|
+
const recreate = allowCreate && snapshot.content === null;
|
|
10482
|
+
if (!recreate && snapshot.content !== expected) return {
|
|
10293
10483
|
ok: false,
|
|
10294
|
-
reason: "
|
|
10484
|
+
reason: "diverged"
|
|
10295
10485
|
};
|
|
10296
|
-
if (
|
|
10486
|
+
if (snapshot.content === null && !recreate) return {
|
|
10297
10487
|
ok: false,
|
|
10298
|
-
reason: "
|
|
10488
|
+
reason: "missing"
|
|
10489
|
+
};
|
|
10490
|
+
const intent = recreate ? { kind: "createIfAbsent" } : {
|
|
10491
|
+
kind: "replaceIfVersion",
|
|
10492
|
+
version: snapshot.version
|
|
10299
10493
|
};
|
|
10300
|
-
const currentStat = await fs.stat(target, signal).catch(() => void 0);
|
|
10301
10494
|
try {
|
|
10302
|
-
await fs.writeText(target, content ?? "",
|
|
10303
|
-
kind: "replaceIfVersion",
|
|
10304
|
-
version: currentStat.version
|
|
10305
|
-
} : void 0, signal, policy);
|
|
10495
|
+
await fs.writeText(target, content ?? "", intent, signal, policy);
|
|
10306
10496
|
const readBack = await readText(fs, target);
|
|
10307
|
-
if (
|
|
10497
|
+
if (content !== readBack) return {
|
|
10308
10498
|
ok: false,
|
|
10309
10499
|
reason: "read-back-mismatch"
|
|
10310
10500
|
};
|
|
@@ -10347,7 +10537,7 @@ async function applyDecide(fs, cwd, request, signal, policy) {
|
|
|
10347
10537
|
blocks: beforeBlocks,
|
|
10348
10538
|
now: Date.now(),
|
|
10349
10539
|
write: (content) => {
|
|
10350
|
-
if (content === null) return deleteTarget(fs, cwd, stored.entry.id, signal);
|
|
10540
|
+
if (content === null) return deleteTarget(fs, cwd, stored.entry.id, stored.entry.newText, signal, policy);
|
|
10351
10541
|
return writerFor(fs, cwd, stored.entry.id, stored.entry.newText, signal, policy)(content);
|
|
10352
10542
|
}
|
|
10353
10543
|
});
|
|
@@ -10575,7 +10765,7 @@ async function undoDecide(fs, cwd, move, signal, policy) {
|
|
|
10575
10765
|
* @returns 写盘结果
|
|
10576
10766
|
*/
|
|
10577
10767
|
async function writeBack(fs, cwd, stored, target, signal, policy) {
|
|
10578
|
-
if (target.flags.missing) return deleteTarget(fs, cwd, stored.entry.id, signal);
|
|
10768
|
+
if (target.flags.missing) return deleteTarget(fs, cwd, stored.entry.id, stored.entry.newText, signal, policy);
|
|
10579
10769
|
return writerFor(fs, cwd, stored.entry.id, stored.entry.newText, signal, policy, true)(target.entry.newText);
|
|
10580
10770
|
}
|
|
10581
10771
|
/**
|
|
@@ -10594,65 +10784,93 @@ function parseSnapshot(text) {
|
|
|
10594
10784
|
}
|
|
10595
10785
|
}
|
|
10596
10786
|
/**
|
|
10597
|
-
* @
|
|
10598
|
-
* @
|
|
10599
|
-
* @
|
|
10600
|
-
* @param
|
|
10601
|
-
* @param
|
|
10787
|
+
* @private
|
|
10788
|
+
* @author ddj 2026年11月04号
|
|
10789
|
+
* @description 版本—内容—版本读取,防止将新版本令牌与旧内容拼成写入依据。
|
|
10790
|
+
* @param fs 文件提供方
|
|
10791
|
+
* @param target 已解析目标
|
|
10602
10792
|
* @param signal 取消信号
|
|
10603
|
-
* @returns
|
|
10793
|
+
* @returns 稳定快照或拒绝原因
|
|
10604
10794
|
*/
|
|
10605
|
-
async function
|
|
10606
|
-
let target;
|
|
10795
|
+
async function stableRead(fs, target, signal) {
|
|
10607
10796
|
try {
|
|
10608
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10797
|
+
const before = await fs.stat(target, signal);
|
|
10798
|
+
if (!before) return {
|
|
10799
|
+
ok: true,
|
|
10800
|
+
content: null
|
|
10801
|
+
};
|
|
10802
|
+
if (before.type !== "file" || !before.version) return {
|
|
10803
|
+
ok: false,
|
|
10804
|
+
reason: "unguarded-file"
|
|
10805
|
+
};
|
|
10806
|
+
const content = await readText(fs, target, signal);
|
|
10807
|
+
if (content === null) return {
|
|
10808
|
+
ok: false,
|
|
10809
|
+
reason: "revision-mismatch"
|
|
10810
|
+
};
|
|
10811
|
+
const after = await fs.stat(target, signal);
|
|
10812
|
+
if (after?.type !== "file" || after.version !== before.version) return {
|
|
10813
|
+
ok: false,
|
|
10814
|
+
reason: "revision-mismatch"
|
|
10815
|
+
};
|
|
10816
|
+
return {
|
|
10817
|
+
ok: true,
|
|
10818
|
+
content,
|
|
10819
|
+
version: before.version
|
|
10820
|
+
};
|
|
10821
|
+
} catch (error) {
|
|
10613
10822
|
return {
|
|
10614
10823
|
ok: false,
|
|
10615
|
-
reason:
|
|
10824
|
+
reason: reasonOf(error)
|
|
10616
10825
|
};
|
|
10617
10826
|
}
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10827
|
+
}
|
|
10828
|
+
/**
|
|
10829
|
+
* @private
|
|
10830
|
+
* @author ddj 2026年11月04号
|
|
10831
|
+
* @description 通过 provider 受控删除,保留会话策略与原子版本校验;旧 Host 不回落直删。
|
|
10832
|
+
* @param fs 文件提供方
|
|
10833
|
+
* @param cwd 工作目录
|
|
10834
|
+
* @param identity 目标身份
|
|
10835
|
+
* @param expected 应有内容
|
|
10836
|
+
* @param signal 取消信号
|
|
10837
|
+
* @param policy 会话沙箱策略
|
|
10838
|
+
* @returns 删除结果;结果不明不提交决定
|
|
10839
|
+
*/
|
|
10840
|
+
async function deleteTarget(fs, cwd, identity, expected, signal, policy) {
|
|
10841
|
+
if (typeof fs.deleteFile !== "function") return {
|
|
10624
10842
|
ok: false,
|
|
10625
|
-
reason: "
|
|
10843
|
+
reason: "delete-unavailable"
|
|
10626
10844
|
};
|
|
10627
10845
|
try {
|
|
10628
|
-
await
|
|
10846
|
+
const target = await fs.resolve(identity, {
|
|
10847
|
+
cwd,
|
|
10848
|
+
signal
|
|
10849
|
+
});
|
|
10850
|
+
const snapshot = await stableRead(fs, target, signal);
|
|
10851
|
+
if (!snapshot.ok) return snapshot;
|
|
10852
|
+
if (snapshot.content === null) return {
|
|
10853
|
+
ok: true,
|
|
10854
|
+
content: null
|
|
10855
|
+
};
|
|
10856
|
+
if (snapshot.content !== expected) return {
|
|
10857
|
+
ok: false,
|
|
10858
|
+
reason: "diverged"
|
|
10859
|
+
};
|
|
10860
|
+
await fs.deleteFile(target, { version: snapshot.version }, signal, policy);
|
|
10861
|
+
return await fs.stat(target, signal) ? {
|
|
10862
|
+
ok: false,
|
|
10863
|
+
reason: "delete-failed"
|
|
10864
|
+
} : {
|
|
10865
|
+
ok: true,
|
|
10866
|
+
content: null
|
|
10867
|
+
};
|
|
10629
10868
|
} catch (error) {
|
|
10630
10869
|
return {
|
|
10631
10870
|
ok: false,
|
|
10632
10871
|
reason: reasonOf(error)
|
|
10633
10872
|
};
|
|
10634
10873
|
}
|
|
10635
|
-
return await fs.stat(target, signal).catch(() => void 0) ? {
|
|
10636
|
-
ok: false,
|
|
10637
|
-
reason: "delete-failed"
|
|
10638
|
-
} : {
|
|
10639
|
-
ok: true,
|
|
10640
|
-
content: null
|
|
10641
|
-
};
|
|
10642
|
-
}
|
|
10643
|
-
/**
|
|
10644
|
-
* @author ddj 2026年10月29号
|
|
10645
|
-
* @description: 判断宿主路径是否位于工作区内,Windows 下按大小写不敏感比较。
|
|
10646
|
-
* @param cwd 工作区
|
|
10647
|
-
* @param absolute 目标绝对路径
|
|
10648
|
-
* @returns 是否在工作区内
|
|
10649
|
-
*/
|
|
10650
|
-
function insideWorkspace(cwd, absolute) {
|
|
10651
|
-
const root = resolve(cwd);
|
|
10652
|
-
const candidate = resolve(absolute);
|
|
10653
|
-
const normalize = (value) => process.platform === "win32" ? value.toLowerCase() : value;
|
|
10654
|
-
const rootKey = normalize(root).endsWith(sep) ? normalize(root) : normalize(root) + sep;
|
|
10655
|
-
return normalize(candidate).startsWith(rootKey);
|
|
10656
10874
|
}
|
|
10657
10875
|
/**
|
|
10658
10876
|
* @author ddj 2026年10月29号
|
|
@@ -16574,7 +16792,7 @@ function reviewHandlers(ctx, registry) {
|
|
|
16574
16792
|
*/
|
|
16575
16793
|
function pendingPageOf(cwd, sessionId) {
|
|
16576
16794
|
try {
|
|
16577
|
-
const page = loadPage(cwd, sessionId
|
|
16795
|
+
const page = loadPage(cwd, sessionId);
|
|
16578
16796
|
return page.items.some((item) => item.settled === true) ? {
|
|
16579
16797
|
...page,
|
|
16580
16798
|
items: page.items.filter((item) => item.settled !== true)
|
|
@@ -16714,8 +16932,7 @@ function reviewHandlers(ctx, registry) {
|
|
|
16714
16932
|
const scope = await scopeOf(args.sessionId);
|
|
16715
16933
|
const cwd = scope.cwd;
|
|
16716
16934
|
if (args.id.startsWith("pending:")) {
|
|
16717
|
-
const
|
|
16718
|
-
const detail = loadDetail(cwd, entryId, addedFiles(cwd, args.sessionId).has(entryId));
|
|
16935
|
+
const detail = loadDetail(cwd, args.id.slice(8));
|
|
16719
16936
|
if (!detail) return {
|
|
16720
16937
|
ok: false,
|
|
16721
16938
|
error: "待审条目不存在"
|
|
@@ -16763,7 +16980,7 @@ function reviewHandlers(ctx, registry) {
|
|
|
16763
16980
|
decision: args.decision,
|
|
16764
16981
|
scope: args.scope === "hunk" ? "block" : "file",
|
|
16765
16982
|
...args.blockId === void 0 ? {} : { blockId: args.blockId }
|
|
16766
|
-
});
|
|
16983
|
+
}, void 0, policyOf(ctx, sessionOf(ctx, args.sessionId)));
|
|
16767
16984
|
if (!result.ok) return {
|
|
16768
16985
|
ok: false,
|
|
16769
16986
|
error: result.reason
|
|
@@ -16901,7 +17118,7 @@ function readHandlers(kit) {
|
|
|
16901
17118
|
return {
|
|
16902
17119
|
"edrv.pending.list": async (args) => {
|
|
16903
17120
|
const cwd = await kit.read(args.sessionId);
|
|
16904
|
-
const page = loadPage(cwd, args.sessionId
|
|
17121
|
+
const page = loadPage(cwd, args.sessionId);
|
|
16905
17122
|
setPendingCovers(cwd, args.sessionId, page.items.map((item) => item.path));
|
|
16906
17123
|
return {
|
|
16907
17124
|
ok: true,
|
|
@@ -16909,9 +17126,7 @@ function readHandlers(kit) {
|
|
|
16909
17126
|
};
|
|
16910
17127
|
},
|
|
16911
17128
|
"edrv.pending.detail": async (args) => {
|
|
16912
|
-
const
|
|
16913
|
-
const rawId = pendingRawId(args.id);
|
|
16914
|
-
const detail = loadDetail(cwd, rawId, addedFiles(cwd, args.sessionId).has(rawId));
|
|
17129
|
+
const detail = loadDetail(await kit.read(args.sessionId), pendingRawId(args.id));
|
|
16915
17130
|
return detail ? {
|
|
16916
17131
|
ok: true,
|
|
16917
17132
|
detail
|