@dsh-electron/dsh-plugin-multi-root-workspace 0.1.11 → 0.1.13
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/CHANGELOG.en.md +25 -1
- package/CHANGELOG.md +25 -1
- package/README.en.md +10 -8
- package/README.md +10 -8
- package/cordis.patch.yml +7 -2
- package/lib/auto-review.js +1 -1
- package/lib/client.js +197 -74
- package/lib/client.js.map +1 -1
- package/lib/{command-BGdz7VBH.js → command-B9855H7y.js} +3 -2
- package/lib/command.js +1 -1
- package/lib/{compat-BiW-m_q0.js → compat-Bg2HFVMX.js} +3 -0
- package/lib/compat.js +1 -1
- package/lib/{sandbox-CXk5h-ct.js → dialects-Dr4rDR-H.js} +6 -264
- package/lib/{fs-BTXySwSG.js → fs-CfzJq2wd.js} +23 -8
- package/lib/fs.js +1 -1
- package/lib/index.js +7 -6
- package/lib/{instructions-MePoRigy.js → instructions-KsUwUFE0.js} +53 -37
- package/lib/instructions.js +1 -1
- package/lib/lsp.js +19 -16
- package/lib/native-win32-2waCRkPI.js +312 -0
- package/lib/process-fence.js +62 -12
- package/lib/protocol-CJOvAvOS.js +652 -0
- package/lib/{registry-KSsmU393.js → registry-Bv431TfO.js} +717 -74
- package/lib/registry-operation-C2ZV3Jhw.js +116 -0
- package/lib/registry.js +3 -2
- package/lib/{review-context-AH-SXHmj.js → review-context-NDpKeMCc.js} +1 -1
- package/lib/review-context.js +1 -1
- package/lib/sandbox-C-vCJCp3.js +278 -0
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +101 -11
- package/lib/skills.js +48 -29
- package/lib/types/authority/native-win32.d.ts +7 -0
- package/lib/types/authority/native.d.ts +23 -0
- package/lib/types/authority/protocol.d.ts +70 -0
- package/lib/types/authority/recovery.d.ts +58 -0
- package/lib/types/client/RootSwitcher.d.ts +6 -3
- package/lib/types/client/files-tree/adapter.d.ts +63 -1
- package/lib/types/compat/authority-storage.d.ts +6 -0
- package/lib/types/compat/dsh-version.d.ts +4 -4
- package/lib/types/compat/open-in-app-device.d.ts +1 -1
- package/lib/types/compat/process-entrypoints.d.ts +2 -0
- package/lib/types/compat/process-profile.d.ts +3 -0
- package/lib/types/compat/sandbox-confine.d.ts +2 -0
- package/lib/types/fs.d.ts +4 -1
- package/lib/types/instructions.d.ts +0 -47
- package/lib/types/registry-operation.d.ts +31 -0
- package/lib/types/registry.d.ts +145 -6
- package/lib/types/scope.d.ts +28 -0
- package/lib/types/skills/catalog.d.ts +3 -0
- package/lib/types/skills/pool.d.ts +1 -1
- package/lib/workspace-files.js +63 -7
- package/package.json +38 -31
package/lib/skills.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as wrapMethod } from "./method-wrapper-CFY3p3j2.js";
|
|
2
|
+
import { c as withRegistryRead } from "./registry-operation-C2ZV3Jhw.js";
|
|
2
3
|
import { r as isPackageInstalled, t as createInstructionMessage } from "./llm-message-fYORacvh.js";
|
|
3
4
|
import { n as skillsMetadataValid } from "./metadata-DgqSdVX_.js";
|
|
4
5
|
import { constants, statSync } from "node:fs";
|
|
@@ -6,8 +7,8 @@ import { createHash } from "node:crypto";
|
|
|
6
7
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
8
|
import { Service } from "@deepseek-ai/cordis";
|
|
8
9
|
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
9
|
-
import { lstat, open, readdir, stat } from "node:fs/promises";
|
|
10
10
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11
|
+
import { lstat, open, readdir, stat } from "node:fs/promises";
|
|
11
12
|
import z from "@deepseek-ai/schemastery";
|
|
12
13
|
//#region \0@oxc-project+runtime@0.149.0/helpers/esm/usingCtx.js
|
|
13
14
|
function _usingCtx() {
|
|
@@ -191,9 +192,10 @@ var SkillReaderPool = class {
|
|
|
191
192
|
return this.entries.size;
|
|
192
193
|
}
|
|
193
194
|
get roots() {
|
|
194
|
-
return [...this.entries.
|
|
195
|
+
return [...new Set([...this.entries.values()].map((entry) => entry.root))];
|
|
195
196
|
}
|
|
196
|
-
async use(
|
|
197
|
+
async use(root, create, run, signal, identity = root) {
|
|
198
|
+
const key = identity;
|
|
197
199
|
for (;;) {
|
|
198
200
|
signal?.throwIfAborted();
|
|
199
201
|
if (this.closing) throw new Error("multi-root skills: reader pool is closing");
|
|
@@ -225,6 +227,7 @@ var SkillReaderPool = class {
|
|
|
225
227
|
const controller = new AbortController();
|
|
226
228
|
entry = {
|
|
227
229
|
key,
|
|
230
|
+
root,
|
|
228
231
|
controller,
|
|
229
232
|
reader: create(controller.signal),
|
|
230
233
|
busy: 0
|
|
@@ -235,7 +238,7 @@ var SkillReaderPool = class {
|
|
|
235
238
|
this.entries.set(key, entry);
|
|
236
239
|
entry.busy++;
|
|
237
240
|
try {
|
|
238
|
-
if (entry.stamp === void 0) entry.stamp = await skillTreeStamp(
|
|
241
|
+
if (entry.stamp === void 0) entry.stamp = await skillTreeStamp(entry.root);
|
|
239
242
|
signal?.throwIfAborted();
|
|
240
243
|
return await run(await entry.reader);
|
|
241
244
|
} finally {
|
|
@@ -249,7 +252,7 @@ var SkillReaderPool = class {
|
|
|
249
252
|
this.polling ??= (async () => {
|
|
250
253
|
let changed = false;
|
|
251
254
|
for (const entry of [...this.entries.values()]) {
|
|
252
|
-
const stamp = await skillTreeStamp(entry.
|
|
255
|
+
const stamp = await skillTreeStamp(entry.root);
|
|
253
256
|
if (entry.stamp !== void 0 && entry.stamp !== stamp) changed = true;
|
|
254
257
|
entry.stamp = stamp;
|
|
255
258
|
}
|
|
@@ -285,10 +288,10 @@ var SkillReaderPool = class {
|
|
|
285
288
|
/** No symlink target outside the Root is read or enumerated. Content reads stay on demand. */
|
|
286
289
|
async function skillTreeStamp(root) {
|
|
287
290
|
const rows = [canonicalPath(root)];
|
|
288
|
-
const stamp = async (path) => {
|
|
291
|
+
const stamp = async (path, identityOnly = false) => {
|
|
289
292
|
try {
|
|
290
293
|
const link = await lstat(path, { bigint: true });
|
|
291
|
-
rows.push(`${path}:${link.dev}:${link.ino}:${link.size}:${link.mtimeNs}:${link.ctimeNs}:${canonicalPath(path)}`);
|
|
294
|
+
rows.push(identityOnly ? `${path}:${link.dev}:${link.ino}:${link.mode}:${canonicalPath(path)}` : `${path}:${link.dev}:${link.ino}:${link.size}:${link.mtimeNs}:${link.ctimeNs}:${canonicalPath(path)}`);
|
|
292
295
|
if (!containsPath(root, canonicalPath(path))) return void 0;
|
|
293
296
|
return await stat(path);
|
|
294
297
|
} catch {
|
|
@@ -296,7 +299,7 @@ async function skillTreeStamp(root) {
|
|
|
296
299
|
return;
|
|
297
300
|
}
|
|
298
301
|
};
|
|
299
|
-
await stamp(root);
|
|
302
|
+
await stamp(root, true);
|
|
300
303
|
for (const directory of [".dsh/skills", ".agents/skills"]) {
|
|
301
304
|
const path = join(root, directory);
|
|
302
305
|
if (!(await stamp(path))?.isDirectory()) continue;
|
|
@@ -336,7 +339,7 @@ var AdditionalSkillCatalog = class {
|
|
|
336
339
|
return this.generation();
|
|
337
340
|
}
|
|
338
341
|
current(primary, id) {
|
|
339
|
-
if (this.signal.aborted || !this.enabled() || this.ctx.multiRootRegistry.
|
|
342
|
+
if (this.signal.aborted || !this.enabled() || !this.ctx.multiRootRegistry.available || this.ctx.multiRootScope.snapshot({
|
|
340
343
|
mode: "read-only",
|
|
341
344
|
workspaceRoot: primary
|
|
342
345
|
}).authority !== "available") return void 0;
|
|
@@ -355,13 +358,17 @@ var AdditionalSkillCatalog = class {
|
|
|
355
358
|
if (root === void 0) return await run(void 0);
|
|
356
359
|
return await this.requests.run({
|
|
357
360
|
primary,
|
|
358
|
-
id
|
|
361
|
+
id,
|
|
362
|
+
epoch: this.ctx.multiRootRegistry.epoch
|
|
359
363
|
}, () => this.pool.use(root.path, (signal) => rootReader(this.ctx, root.path, AbortSignal.any([signal, this.signal]), () => {
|
|
360
364
|
const request = this.requests.getStore();
|
|
361
|
-
return request !== void 0 && this.current(request.primary, request.id)?.path === root.path;
|
|
362
|
-
}), run, options.signal));
|
|
365
|
+
return request !== void 0 && request.epoch === this.ctx.multiRootRegistry.epoch && this.current(request.primary, request.id)?.path === root.path;
|
|
366
|
+
}), run, options.signal, `${this.ctx.multiRootRegistry.epoch ?? "unavailable"}\0${root.path}`));
|
|
363
367
|
}
|
|
364
368
|
async list(primary, options) {
|
|
369
|
+
return withRegistryRead(this.ctx, primary, "skills", () => this.listAdmitted(primary, options));
|
|
370
|
+
}
|
|
371
|
+
async listAdmitted(primary, options) {
|
|
365
372
|
options.signal?.throwIfAborted();
|
|
366
373
|
if (!this.enabled()) return [];
|
|
367
374
|
const roots = await this.ctx.multiRootRegistry.refresh(primary);
|
|
@@ -381,6 +388,7 @@ var AdditionalSkillCatalog = class {
|
|
|
381
388
|
seen.add(candidate.name);
|
|
382
389
|
const locator = {
|
|
383
390
|
primary,
|
|
391
|
+
authorityEpoch: this.ctx.multiRootRegistry.epoch,
|
|
384
392
|
rootId: root.id,
|
|
385
393
|
aliasId: root.aliasId,
|
|
386
394
|
candidate
|
|
@@ -408,8 +416,11 @@ var AdditionalSkillCatalog = class {
|
|
|
408
416
|
}
|
|
409
417
|
/** Locators are created here and are not accepted from a client path or object. */
|
|
410
418
|
async load(primary, entry, options, user) {
|
|
419
|
+
return withRegistryRead(this.ctx, primary, "skills", () => this.loadAdmitted(primary, entry, options, user));
|
|
420
|
+
}
|
|
421
|
+
async loadAdmitted(primary, entry, options, user) {
|
|
411
422
|
const locator = entry.locator;
|
|
412
|
-
if (typeof locator !== "object" || locator === null || !this.locators.has(locator) || locator.primary !== primary) return void 0;
|
|
423
|
+
if (typeof locator !== "object" || locator === null || !this.locators.has(locator) || locator.primary !== primary || locator.authorityEpoch !== this.ctx.multiRootRegistry.epoch) return void 0;
|
|
413
424
|
const root = this.current(primary, locator.rootId);
|
|
414
425
|
if (root === void 0 || root.aliasId !== locator.aliasId || entry.name !== internalSkillName(primary, root.id, locator.candidate.name)) return void 0;
|
|
415
426
|
const revision = this.generation();
|
|
@@ -446,14 +457,16 @@ var AdditionalSkillCatalog = class {
|
|
|
446
457
|
}
|
|
447
458
|
async admissionValid(definition) {
|
|
448
459
|
const receipt = this.loadedReceipts.get(definition);
|
|
449
|
-
if (receipt === void 0) return false;
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
460
|
+
if (receipt === void 0 || receipt.locator.authorityEpoch !== this.ctx.multiRootRegistry.epoch) return false;
|
|
461
|
+
return withRegistryRead(this.ctx, receipt.locator.primary, "skills", async () => {
|
|
462
|
+
const current = this.current(receipt.locator.primary, receipt.locator.rootId);
|
|
463
|
+
if (current === void 0 || current.aliasId !== receipt.locator.aliasId || receipt.revision !== this.generation()) return false;
|
|
464
|
+
try {
|
|
465
|
+
return receipt.stamp === await fileStamp(definition.path) && receipt.revision === this.generation() && this.current(receipt.locator.primary, receipt.locator.rootId)?.aliasId === receipt.locator.aliasId;
|
|
466
|
+
} catch {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
});
|
|
457
470
|
}
|
|
458
471
|
async dispose() {
|
|
459
472
|
await this.pool.dispose();
|
|
@@ -574,16 +587,22 @@ function installSkillRouting(ctx) {
|
|
|
574
587
|
fibers.push(ctx.inject(["skills"], (scope) => {
|
|
575
588
|
const registry = scope.skills;
|
|
576
589
|
scope.effect(() => wrapMethod(registry, "snapshot", (original) => async function(options = {}) {
|
|
577
|
-
const
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
if (
|
|
582
|
-
|
|
583
|
-
|
|
590
|
+
const primaryOrigin = skillOrigin(scope, options) ?? cold.getStore();
|
|
591
|
+
const run = async () => {
|
|
592
|
+
const result = await original.call(this, optionsFor(options));
|
|
593
|
+
const primary = skillOrigin(scope, options);
|
|
594
|
+
if (primary !== void 0) for (const skill of result.skills) {
|
|
595
|
+
if (skill.provider === "multi-root-workspace") continue;
|
|
596
|
+
if (scope.multiRootRegistry.list(primary).some((root) => skill.name.startsWith(internalSkillName(primary, root.id, "")))) scope.logger.warn(`multi-root skills: internal ID conflict; native winner retained: ${skill.name} (${skill.provider})`);
|
|
597
|
+
}
|
|
598
|
+
return result;
|
|
599
|
+
};
|
|
600
|
+
return primaryOrigin === void 0 ? run() : withRegistryRead(scope, primaryOrigin, "skills", run);
|
|
584
601
|
}));
|
|
585
602
|
scope.effect(() => wrapMethod(registry, "get", (original) => function(name, options = {}) {
|
|
586
|
-
|
|
603
|
+
const primary = skillOrigin(scope, options) ?? cold.getStore();
|
|
604
|
+
const run = () => original.call(this, name, optionsFor(options));
|
|
605
|
+
return primary === void 0 ? run() : withRegistryRead(scope, primary, "skills", run);
|
|
587
606
|
}));
|
|
588
607
|
}));
|
|
589
608
|
fibers.push(ctx.inject(["sessionSkillCatalog", "sessionQuery"], (scope) => {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Channel, Listener } from './native.ts';
|
|
2
|
+
export declare function connectWindows(endpoint: string): Promise<Channel>;
|
|
3
|
+
export declare function listenWindows(endpoint: string, accepted: (channel: Channel) => void): Promise<Listener>;
|
|
4
|
+
export declare function windowsHostAvailability(): Promise<{
|
|
5
|
+
available: boolean;
|
|
6
|
+
reason?: string;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { EventEmitter } from 'node:events';
|
|
2
|
+
export interface Channel {
|
|
3
|
+
on: EventEmitter['on'];
|
|
4
|
+
readonly writableLength?: number;
|
|
5
|
+
write(data: string): boolean;
|
|
6
|
+
destroy(): unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface Listener {
|
|
9
|
+
close(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function storeIdentity(root: string): {
|
|
12
|
+
root: string;
|
|
13
|
+
id: string;
|
|
14
|
+
endpoint: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function connectNative(endpoint: string): Promise<Channel>;
|
|
17
|
+
/** Called only by the kernel lease holder. Never removes another epoch's socket. */
|
|
18
|
+
export declare function listenNative(endpoint: string, accepted: (channel: Channel) => void): Promise<Listener>;
|
|
19
|
+
/** Capability probe for native tests and diagnostics; not an authorization bypass. */
|
|
20
|
+
export declare function nativeHostAvailability(): Promise<{
|
|
21
|
+
available: boolean;
|
|
22
|
+
reason?: string;
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const methods: readonly ["hello", "snapshot", "admit", "add", "removeAt", "setAccess", "setAlias", "setSkillsEnabled", "setAliasId", "move", "recheck"];
|
|
3
|
+
export type Method = typeof methods[number];
|
|
4
|
+
export declare const snapshotSchema: z.ZodObject<{
|
|
5
|
+
protocol: z.ZodLiteral<1>;
|
|
6
|
+
store: z.ZodString;
|
|
7
|
+
epoch: z.ZodString;
|
|
8
|
+
revision: z.ZodNumber;
|
|
9
|
+
primary: z.ZodString;
|
|
10
|
+
roots: z.ZodArray<z.ZodObject<{
|
|
11
|
+
id: z.ZodString;
|
|
12
|
+
path: z.ZodString;
|
|
13
|
+
access: z.ZodEnum<{
|
|
14
|
+
inherit: "inherit";
|
|
15
|
+
"read-only": "read-only";
|
|
16
|
+
"read-write": "read-write";
|
|
17
|
+
}>;
|
|
18
|
+
addedAt: z.ZodString;
|
|
19
|
+
recordedPath: z.ZodOptional<z.ZodString>;
|
|
20
|
+
readOnlyRecordedPath: z.ZodOptional<z.ZodString>;
|
|
21
|
+
readWriteRecordedPath: z.ZodOptional<z.ZodString>;
|
|
22
|
+
alias: z.ZodOptional<z.ZodString>;
|
|
23
|
+
aliasId: z.ZodOptional<z.ZodUnknown>;
|
|
24
|
+
skillsEnabled: z.ZodOptional<z.ZodUnknown>;
|
|
25
|
+
state: z.ZodEnum<{
|
|
26
|
+
available: "available";
|
|
27
|
+
missing: "missing";
|
|
28
|
+
redirected: "redirected";
|
|
29
|
+
invalid: "invalid";
|
|
30
|
+
}>;
|
|
31
|
+
detail: z.ZodOptional<z.ZodString>;
|
|
32
|
+
invalidReason: z.ZodOptional<z.ZodLiteral<"membership-conflict">>;
|
|
33
|
+
}, z.core.$strict>>;
|
|
34
|
+
available: z.ZodBoolean;
|
|
35
|
+
}, z.core.$strict>;
|
|
36
|
+
export type Snapshot = z.infer<typeof snapshotSchema>;
|
|
37
|
+
export declare class AuthorityRpcError extends Error {
|
|
38
|
+
readonly code: string;
|
|
39
|
+
constructor(code: string, message: string);
|
|
40
|
+
}
|
|
41
|
+
export declare class AuthorityClient {
|
|
42
|
+
private readonly channel;
|
|
43
|
+
private readonly store;
|
|
44
|
+
private readonly limit;
|
|
45
|
+
private readonly pending;
|
|
46
|
+
private closed;
|
|
47
|
+
epoch: string | undefined;
|
|
48
|
+
private constructor();
|
|
49
|
+
static connect(endpoint: string, store: string, limit: number, onChange: (snapshot: Snapshot) => void, disconnected: () => void, signal?: AbortSignal): Promise<AuthorityClient>;
|
|
50
|
+
request(method: Method, args: readonly unknown[]): Promise<unknown>;
|
|
51
|
+
close(): void;
|
|
52
|
+
}
|
|
53
|
+
export declare class AuthorityServer {
|
|
54
|
+
private readonly store;
|
|
55
|
+
private readonly epoch;
|
|
56
|
+
private readonly limit;
|
|
57
|
+
private readonly dispatch;
|
|
58
|
+
private listener;
|
|
59
|
+
private readonly peers;
|
|
60
|
+
private readonly readyPeers;
|
|
61
|
+
private closed;
|
|
62
|
+
private inFlight;
|
|
63
|
+
private constructor();
|
|
64
|
+
static listen(endpoint: string, store: string, epoch: string, limit: number, dispatch: (method: Method, args: unknown[]) => Promise<unknown>): Promise<AuthorityServer>;
|
|
65
|
+
private accept;
|
|
66
|
+
changed(snapshot: Snapshot): void;
|
|
67
|
+
close(): void;
|
|
68
|
+
}
|
|
69
|
+
export declare function parseArguments(method: Method, args: unknown[]): unknown[];
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded background retry for the registry authority: one pending timer and
|
|
3
|
+
* one in-flight attempt per instance, post-completion backoff, and local
|
|
4
|
+
* cancellation on dispose. The scheduler owns NO policy — the caller supplies
|
|
5
|
+
* `run` (one recovery attempt) and `eligible` (whether a degraded state still
|
|
6
|
+
* wants recovery), so the same object can drive contended, follower
|
|
7
|
+
* resync, and retryable storage failures.
|
|
8
|
+
*
|
|
9
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/authority/recovery
|
|
10
|
+
*/
|
|
11
|
+
/** Injectable clock so fake-timer specs can drive the scheduler exactly. */
|
|
12
|
+
export interface RecoveryTimers {
|
|
13
|
+
setTimeout: typeof setTimeout;
|
|
14
|
+
clearTimeout: typeof clearTimeout;
|
|
15
|
+
random(): number;
|
|
16
|
+
now(): number;
|
|
17
|
+
}
|
|
18
|
+
/** What the scheduler is doing, for diagnostics surfaces. */
|
|
19
|
+
export type RecoveryPhase = 'idle' | 'waiting' | 'attempting' | 'disabled';
|
|
20
|
+
export declare class RecoveryScheduler {
|
|
21
|
+
private readonly run;
|
|
22
|
+
private readonly eligible;
|
|
23
|
+
private readonly enabled;
|
|
24
|
+
private readonly timers;
|
|
25
|
+
private timer;
|
|
26
|
+
private inFlight;
|
|
27
|
+
private disposed;
|
|
28
|
+
private attempts;
|
|
29
|
+
private retryAt;
|
|
30
|
+
constructor(options: {
|
|
31
|
+
run: () => Promise<void>;
|
|
32
|
+
eligible: () => boolean;
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
timers?: RecoveryTimers;
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Ask for a recovery attempt. Called after every `ensureAuthority()` and on
|
|
38
|
+
* every downgrade event. A healthy state (`!eligible()`) resets the backoff;
|
|
39
|
+
* a pending timer or in-flight attempt is never duplicated or reset.
|
|
40
|
+
*/
|
|
41
|
+
arm(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Progress event (a follower connection was just adopted): the backoff is
|
|
44
|
+
* earned, so reset it and run the next attempt immediately — still through
|
|
45
|
+
* the single pending timer.
|
|
46
|
+
*/
|
|
47
|
+
kick(): void;
|
|
48
|
+
/** Stop scheduling. An in-flight attempt is left to finish on its own. */
|
|
49
|
+
dispose(): void;
|
|
50
|
+
get state(): {
|
|
51
|
+
phase: RecoveryPhase;
|
|
52
|
+
attempts: number;
|
|
53
|
+
nextRetryAt: number | undefined;
|
|
54
|
+
};
|
|
55
|
+
private clearTimer;
|
|
56
|
+
private delay;
|
|
57
|
+
private tick;
|
|
58
|
+
}
|
|
@@ -32,9 +32,12 @@
|
|
|
32
32
|
* the same element's `closest('[data-dockkit-content]')` — the dockkit
|
|
33
33
|
* tabHost section — with a generated fallback where the hook is absent.
|
|
34
34
|
*
|
|
35
|
-
* The selected root
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* The selected root lives in the inject product's session-scoped snapshot,
|
|
36
|
+
* keyed by that dockkit tab id — component state could not survive the
|
|
37
|
+
* remount every file open causes (the opened tab takes the pane, the Files
|
|
38
|
+
* body unmounts with the whole occupant subtree). One selection per tab;
|
|
39
|
+
* session switches mint a fresh inject product, so no selection crosses
|
|
40
|
+
* sessions, and a registry eviction reconciles it back to primary.
|
|
38
41
|
*
|
|
39
42
|
* @module @dsh-electron/dsh-plugin-multi-root-workspace/client/RootSwitcher
|
|
40
43
|
*/
|
|
@@ -25,19 +25,72 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
27
27
|
import type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client';
|
|
28
|
-
import type
|
|
28
|
+
import { type BoundActions, type SnapshotSelectorHook } from '@deepseek-ai/dsh-client-store';
|
|
29
|
+
import type { HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
|
|
30
|
+
import type { TabId } from '@deepseek-ai/dsh-client-ui-dockkit';
|
|
29
31
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
30
32
|
import { type FilesInjected } from './face.ts';
|
|
31
33
|
import { createFilesStore } from './store.ts';
|
|
32
34
|
import { filesTreeSurfaceSupported } from './primitives.ts';
|
|
33
35
|
import type { FilesTreeProps } from './FilesTree.tsx';
|
|
36
|
+
import type { RootView } from '../../contract.ts';
|
|
34
37
|
import { NS } from './locales.ts';
|
|
38
|
+
/**
|
|
39
|
+
* One session's per-tab additional-root selections, keyed by the pane's
|
|
40
|
+
* dockkit tab id. An absent entry means the primary root.
|
|
41
|
+
*/
|
|
42
|
+
export type TreeSelection = Readonly<Record<string, RootView>>;
|
|
43
|
+
/**
|
|
44
|
+
* The lifetimes a selection runs under, keyed by tab like the store bucket:
|
|
45
|
+
* while an entry's signal is live the tree keeps its loaded levels, watches,
|
|
46
|
+
* expansion, and scroll — exactly what upstream's tab-record signal gives the
|
|
47
|
+
* native tree across a body remount (opening a file deactivates this tab,
|
|
48
|
+
* unmounting its body without ending the record).
|
|
49
|
+
*/
|
|
50
|
+
export interface FilesTreeTakeover {
|
|
51
|
+
/**
|
|
52
|
+
* The live signal for (tabId, root); reuses the live entry so a remounted
|
|
53
|
+
* body finds its bucket and watches intact. A different root — or an entry
|
|
54
|
+
* already aborted — ends the previous controller before minting the next.
|
|
55
|
+
* @param tabId - the pane's dockkit tab id.
|
|
56
|
+
* @param root - absolute path of the root being browsed.
|
|
57
|
+
* @param section - the dockkit tabHost section the caller mounted under; a
|
|
58
|
+
* live entry keeps an observer on it that ends the takeover whenever the
|
|
59
|
+
* section leaves the document — the record's real end — including when the
|
|
60
|
+
* body that mounted it has long been unmounted itself.
|
|
61
|
+
* @returns the signal the forked tree lives under.
|
|
62
|
+
*/
|
|
63
|
+
readonly signal: (tabId: TabId, root: string, section?: Element) => AbortSignal;
|
|
64
|
+
/**
|
|
65
|
+
* Abort and drop one tab's takeover: the reader deselected or switched
|
|
66
|
+
* roots, or the tab record itself went away.
|
|
67
|
+
* @param tabId - the pane's dockkit tab id.
|
|
68
|
+
*/
|
|
69
|
+
readonly end: (tabId: TabId) => void;
|
|
70
|
+
}
|
|
35
71
|
/** The inject face the registration composes onto the occupant's props. */
|
|
36
72
|
export interface FilesTreeInjected {
|
|
37
73
|
/** The verbatim files face (list/watch bookkeeping included). */
|
|
38
74
|
readonly face: FilesInjected;
|
|
39
75
|
/** Open one `dsh-resource://file` address in the host viewer. */
|
|
40
76
|
readonly openFile: (address: string) => void;
|
|
77
|
+
/**
|
|
78
|
+
* The reserved inject compartment: the slot renderer binds each source to a
|
|
79
|
+
* `use<Name>` selector hook, so `treeSelection` reaches the component as
|
|
80
|
+
* `useTreeSelection`, never as a prop under its own key.
|
|
81
|
+
*/
|
|
82
|
+
readonly hooks: {
|
|
83
|
+
readonly treeSelection: HostObservable<TreeSelection>;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Persist one pane's selected root; `'primary'` drops its entry. The
|
|
87
|
+
* session-scoped snapshot survives the body remount a file open causes.
|
|
88
|
+
* @param tabId - the pane's dockkit tab id.
|
|
89
|
+
* @param root - the selected root, or `'primary'` to browse the session root.
|
|
90
|
+
*/
|
|
91
|
+
readonly selectRoot: (tabId: TabId, root: RootView | 'primary') => void;
|
|
92
|
+
/** The forked tree's lifetimes; see {@link FilesTreeTakeover}. */
|
|
93
|
+
readonly takeover: FilesTreeTakeover;
|
|
41
94
|
}
|
|
42
95
|
/** Everything `FilesTree` needs that the slot framework does not synthesize. */
|
|
43
96
|
export interface FilesTreeSurface {
|
|
@@ -47,6 +100,12 @@ export interface FilesTreeSurface {
|
|
|
47
100
|
readonly face: FilesInjected;
|
|
48
101
|
readonly openFile: (address: string) => void;
|
|
49
102
|
readonly t: FilesTreeProps['t'];
|
|
103
|
+
/** Selector hook over this session's per-tab root selections. */
|
|
104
|
+
readonly useTreeSelection: SnapshotSelectorHook<TreeSelection>;
|
|
105
|
+
/** The selection's write side; see {@link FilesTreeInjected.selectRoot}. */
|
|
106
|
+
readonly selectRoot: FilesTreeInjected['selectRoot'];
|
|
107
|
+
/** The forked tree's lifetimes; see {@link FilesTreeTakeover}. */
|
|
108
|
+
readonly takeover: FilesTreeTakeover;
|
|
50
109
|
}
|
|
51
110
|
/**
|
|
52
111
|
* A `ClientRemote` stand-in that resolves the real service per call.
|
|
@@ -89,6 +148,9 @@ export declare function createFilesTreeSurface(props: {
|
|
|
89
148
|
readonly actions: FilesTreeSurface['actions'];
|
|
90
149
|
readonly face: FilesInjected | undefined;
|
|
91
150
|
readonly openFile: ((address: string) => void) | undefined;
|
|
151
|
+
readonly useTreeSelection: FilesTreeSurface['useTreeSelection'] | undefined;
|
|
152
|
+
readonly selectRoot: FilesTreeSurface['selectRoot'] | undefined;
|
|
153
|
+
readonly takeover: FilesTreeSurface['takeover'] | undefined;
|
|
92
154
|
}, t: FilesTreeProps['t']): FilesTreeSurface | undefined;
|
|
93
155
|
export { filesTreeSurfaceSupported, NS as FILES_TREE_NS };
|
|
94
156
|
export { FilesTree } from './FilesTree.tsx';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { DomainSpec } from '@deepseek-ai/dsh-storage-domain';
|
|
3
|
+
export declare function openAuthorityStorage<S extends DomainSpec>(ctx: Context, root: string, spec: S): Promise<{
|
|
4
|
+
table: <N extends keyof S["tables"] & string>(name: N) => import("@deepseek-ai/dsh-storage-domain").KvTable<import("@deepseek-ai/dsh-storage-domain").TableKeyOf<S, N>, import("@deepseek-ai/dsh-storage-domain").TableValueOf<S, N>>;
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
}>;
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* the upgrade smoke passed on that exact version — a green CI run on an
|
|
29
29
|
* unlisted release is evidence, not authorization (ADR-0009).
|
|
30
30
|
*/
|
|
31
|
-
export declare const SUPPORTED_DSH_RELEASES: readonly ["0.1.5-rc.2", "0.1.6-alpha.1", "0.1.6-alpha.2", "0.1.7-alpha.1", "0.1.7-alpha.2", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2", "0.2.1-alpha.1", "0.2.1-alpha.2", "0.2.1-alpha.3", "0.2.1-alpha.4", "0.2.1-alpha.5", "0.2.1-rc.1", "0.2.1-rc.2", "0.2.1-rc.3", "0.2.1-rc.4", "0.2.2-alpha.2"];
|
|
31
|
+
export declare const SUPPORTED_DSH_RELEASES: readonly ["0.1.5-rc.2", "0.1.6-alpha.1", "0.1.6-alpha.2", "0.1.7-alpha.1", "0.1.7-alpha.2", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2", "0.2.1-alpha.1", "0.2.1-alpha.2", "0.2.1-alpha.3", "0.2.1-alpha.4", "0.2.1-alpha.5", "0.2.1-rc.1", "0.2.1-rc.2", "0.2.1-rc.3", "0.2.1-rc.4", "0.2.1-rc.5", "0.2.2-alpha.2"];
|
|
32
32
|
/** One release this plugin claims to support. */
|
|
33
33
|
export type SupportedDshRelease = typeof SUPPORTED_DSH_RELEASES[number];
|
|
34
34
|
/**
|
|
@@ -42,7 +42,7 @@ export declare const REQUIRED_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@
|
|
|
42
42
|
* Absence is fine; a version that DISAGREES with the required packages is not —
|
|
43
43
|
* that is the mixed-installation failure this contract exists to catch.
|
|
44
44
|
*/
|
|
45
|
-
export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-api-session-controller", "@deepseek-ai/dsh-session-query", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
45
|
+
export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-storage-json", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-api-session-controller", "@deepseek-ai/dsh-session-query", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
46
46
|
/**
|
|
47
47
|
* The optional packages this plugin loads itself at run time, through dynamic
|
|
48
48
|
* `import()`s inside this package's own modules. The copy this module resolves
|
|
@@ -50,7 +50,7 @@ export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-skill",
|
|
|
50
50
|
* private copy inside this package's own tree genuinely loads, and a version
|
|
51
51
|
* that disagrees with the host is a real mixed installation, not a phantom.
|
|
52
52
|
*/
|
|
53
|
-
export declare const RUNTIME_OPTIONAL_PACKAGES: readonly ["@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-typert-protocol"];
|
|
53
|
+
export declare const RUNTIME_OPTIONAL_PACKAGES: readonly ["@deepseek-ai/dsh-storage-json", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-typert-protocol"];
|
|
54
54
|
/**
|
|
55
55
|
* The optional packages this plugin consumes only as mounted host services or
|
|
56
56
|
* ambient types — it never imports them, so a copy inside this package's own
|
|
@@ -79,7 +79,7 @@ export declare const HOST_SERVICE_PACKAGES: readonly ["@deepseek-ai/dsh-settings
|
|
|
79
79
|
*/
|
|
80
80
|
export declare const RUNNER_ANCHORED_PACKAGES: readonly ["@deepseek-ai/dsh-sandbox-windows-acl"];
|
|
81
81
|
/** Every package the contract inspects, required first. */
|
|
82
|
-
export declare const CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-fs-local", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-local", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-api-session-controller", "@deepseek-ai/dsh-session-query", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
82
|
+
export declare const CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-fs-local", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-local", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-storage-json", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-api-session-controller", "@deepseek-ai/dsh-session-query", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
83
83
|
/** How this plugin reacts to an installation it does not support. */
|
|
84
84
|
export type CompatEnforcement = 'enforce' | 'warn';
|
|
85
85
|
/** The environment variable that relaxes the gate. */
|
|
@@ -29,3 +29,5 @@ export declare function shellEntrypoints(shell: object): readonly ProcessEntrypo
|
|
|
29
29
|
* @returns the present entries — a structural probe only.
|
|
30
30
|
*/
|
|
31
31
|
export declare function ptcEntrypoints(runtime: object): readonly ProcessEntrypoint[];
|
|
32
|
+
/** Async public entries can acquire admission before calling their implementation. */
|
|
33
|
+
export declare function isAsyncProcessEntry(entry: unknown): boolean;
|
|
@@ -58,3 +58,5 @@ export declare function mapConfined<T>(value: UpstreamConfined, map: (confined:
|
|
|
58
58
|
* @returns the widened result, in the shape the installed release uses.
|
|
59
59
|
*/
|
|
60
60
|
export declare function widenConfined(base: ConfineCall, widen: (confined: ConfinedArgv) => ConfinedArgv, argv: readonly string[], policy: SandboxPolicy, signal?: AbortSignal): UpstreamConfined;
|
|
61
|
+
/** Public consumer call with the installed provider's arity and return shape. */
|
|
62
|
+
export declare function confineInstalled(provider: Pick<LocalSandboxProvider, 'confine'>, argv: readonly string[], policy: SandboxPolicy, signal?: AbortSignal): ConfinedArgv | Promise<ConfinedArgv>;
|
package/lib/types/fs.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export declare class MultiRootFileSystem extends LocalFileSystem {
|
|
|
64
64
|
editText(target: FsTarget, edit: FsEditRequest, expected?: {
|
|
65
65
|
version: FsVersion;
|
|
66
66
|
}, signal?: AbortSignal, sandboxPolicy?: SandboxExecutionPolicy): Promise<FsEditOutcome>;
|
|
67
|
+
/** The provider must also work without the gated registry (ADR-0009). */
|
|
68
|
+
private withOperation;
|
|
67
69
|
/**
|
|
68
70
|
* The writable roots of one confined call: the upstream derivation (primary
|
|
69
71
|
* root plus the platform temp areas, canonical and deduplicated — empty
|
|
@@ -104,7 +106,8 @@ export declare class MultiRootFileSystem extends LocalFileSystem {
|
|
|
104
106
|
* to primary-root-only writes (no shared temp or additional-root grants)
|
|
105
107
|
* until the authority returns.
|
|
106
108
|
* @param target - the resolved target about to be mutated.
|
|
107
|
-
* @param
|
|
109
|
+
* @param policy - the per-call policy captured before admission.
|
|
110
|
+
* @param scope - the admission snapshot shared by every fence check.
|
|
108
111
|
* @returns the fresh canonical target the mutation must use.
|
|
109
112
|
*/
|
|
110
113
|
private checkedTarget;
|
|
@@ -1,50 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `multi-root-instructions`: the additional roots' own instruction files, made
|
|
3
|
-
* visible to the model.
|
|
4
|
-
*
|
|
5
|
-
* Upstream `agent-instructions` discovers instruction files by walking UP from
|
|
6
|
-
* the session cwd (plus the one user-global file). In a multi-root workspace
|
|
7
|
-
* that walk never reaches an additional root, so `repo-b/AGENTS.md` is
|
|
8
|
-
* invisible to a model that is nevertheless allowed to write `repo-b`. This row
|
|
9
|
-
* closes exactly that gap, and nothing else: the primary root's chain — nested
|
|
10
|
-
* files included — and the user-global file stay upstream's business.
|
|
11
|
-
*
|
|
12
|
-
* Four deliberate choices:
|
|
13
|
-
*
|
|
14
|
-
* 1. **Not a system-prompt contribution.** Instruction text is
|
|
15
|
-
* producer-supplied context, not system authority, so it enters as a
|
|
16
|
-
* user-role message tagged `form: 'instructions'`. Through session format 3
|
|
17
|
-
* the source kind is `plugin`; format 4 retired that shared kind, so the
|
|
18
|
-
* adapter persists this plugin's own (`multi-root-workspace`). It is also
|
|
19
|
-
* the only channel that is logged, and model-visible content must be logged.
|
|
20
|
-
* 2. **Injected from `agent/pre-step`, not a session lifecycle event.**
|
|
21
|
-
* `pre-step` is an awaited waterfall, so discovery, reading and rendering all
|
|
22
|
-
* complete BEFORE the step it feeds — deterministically, on the first step.
|
|
23
|
-
* A synchronous prompt callback could not await them, and an emit-mode
|
|
24
|
-
* lifecycle listener would race the first step. It is also the one hook whose
|
|
25
|
-
* shape is identical across every supported release, which keeps the
|
|
26
|
-
* instruction feature out of the compatibility matrix (ADR-0009).
|
|
27
|
-
* 3. **Discovery is pinned to the root.** `projectRoot` is the additional root,
|
|
28
|
-
* so the walk can never leave it, and every candidate is then required to be
|
|
29
|
-
* canonically INSIDE that root. That is what keeps `$DSH_HOME/AGENTS.md`, the
|
|
30
|
-
* primary root's `AGENTS.md`, and any ancestor's file from being injected a
|
|
31
|
-
* second time by this plugin.
|
|
32
|
-
* 4. **Nested files are driven by the session's own tool activity.** A
|
|
33
|
-
* subdirectory's `AGENTS.md` only matters once the session has worked in that
|
|
34
|
-
* subdirectory, so the directories worth examining are derived from the
|
|
35
|
-
* session's persisted events: `tool/call` and `tool/result` are paired, and a
|
|
36
|
-
* successful `read`/`write`/`edit` makes the path from the root down to the
|
|
37
|
-
* touched file's directory relevant (ADR-0010). A file that disappears is
|
|
38
|
-
* retracted explicitly, because the text it contributed earlier is still in
|
|
39
|
-
* the conversation and silence does not withdraw it.
|
|
40
|
-
*
|
|
41
|
-
* The byte budget is shared: `maxBytes` bounds the WHOLE additional-root
|
|
42
|
-
* snapshot, not each root, because ten roots must not quietly cost ten budgets
|
|
43
|
-
* of context. Roots consume it in scope order. Retractions are exempt, because a
|
|
44
|
-
* truncated withdrawal is worse than a slightly over-budget message.
|
|
45
|
-
*
|
|
46
|
-
* @module @dsh-electron/dsh-plugin-multi-root-workspace/instructions
|
|
47
|
-
*/
|
|
48
1
|
import { Context } from '@deepseek-ai/cordis';
|
|
49
2
|
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
50
3
|
declare module '@deepseek-ai/cordis' {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox';
|
|
2
|
+
import type { FilesystemScope } from './scope.ts';
|
|
3
|
+
export type RegistryOperationKind = 'fs-write' | 'fs-edit' | 'process' | 'pty' | 'lsp' | 'files' | 'instructions' | 'skills';
|
|
4
|
+
export declare function currentRegistryOperation(proof: object): RegistryOperation | undefined;
|
|
5
|
+
export interface RegistryOperationIdentity {
|
|
6
|
+
readonly operationId: string;
|
|
7
|
+
readonly kind: RegistryOperationKind;
|
|
8
|
+
readonly mode: SandboxMode | undefined;
|
|
9
|
+
readonly primaryRoot: string;
|
|
10
|
+
/** Absent when the registry cannot grant additional authority. */
|
|
11
|
+
readonly authorityEpoch: string | undefined;
|
|
12
|
+
/** Effective publication revision, meaningful only within this epoch. */
|
|
13
|
+
readonly revision: number;
|
|
14
|
+
}
|
|
15
|
+
export interface RegistryOperation {
|
|
16
|
+
readonly identity: RegistryOperationIdentity;
|
|
17
|
+
/** Only readable during this operation's callback. */
|
|
18
|
+
readonly scope: FilesystemScope;
|
|
19
|
+
}
|
|
20
|
+
/** Capture by value: neither later publication nor a caller can mutate admission. */
|
|
21
|
+
export declare function freezeOperationScope(scope: FilesystemScope): FilesystemScope;
|
|
22
|
+
/** Execute outside the authority queue; completion also expires async descendants. */
|
|
23
|
+
export declare function runRegistryOperation<T>(scope: FilesystemScope, kind: RegistryOperationKind, authorityEpoch: string | undefined, revision: number, body: (operation: RegistryOperation) => T | Promise<T>, proof?: object, mode?: SandboxMode): Promise<T>;
|
|
24
|
+
/** Optional Registry seam for public consumers; the legacy composition stays exact. */
|
|
25
|
+
export declare function withRegistryRead<T>(ctx: import('@deepseek-ai/cordis').Context, primary: string, kind: RegistryOperationKind, body: () => T | Promise<T>): Promise<T>;
|
|
26
|
+
export declare function withRegistryAdmission<T>(ctx: import('@deepseek-ai/cordis').Context, policy: import('@deepseek-ai/dsh-sandbox').SandboxExecutionPolicy, kind: RegistryOperationKind, body: () => T | Promise<T>): Promise<T>;
|
|
27
|
+
/** Preserve a legacy synchronous entry while granting only the safe primary-root fallback. */
|
|
28
|
+
export declare function runPrimaryOperation<T>(scope: FilesystemScope, mode: SandboxMode, proof: object, body: () => T): T;
|
|
29
|
+
/** Only profiles produced within this exact admission can reach a sync spawn. */
|
|
30
|
+
export declare function rememberOperationProfile(proof: object, argv: readonly string[], capped: boolean): void;
|
|
31
|
+
export declare function operationPermitsProfile(proof: object, argv: readonly string[], capped: boolean): boolean;
|