@dsh-electron/dsh-plugin-multi-root-workspace 0.1.11 → 0.1.12
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 +13 -1
- package/CHANGELOG.md +13 -1
- package/README.en.md +9 -8
- package/README.md +9 -8
- package/cordis.patch.yml +3 -2
- package/lib/auto-review.js +1 -1
- package/lib/client.js +96 -49
- package/lib/client.js.map +1 -1
- package/lib/{command-BGdz7VBH.js → command-CmLekG1M.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-4zdwV7u4.js +641 -0
- package/lib/{registry-KSsmU393.js → registry-YTUOjmVu.js} +368 -25
- 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 +65 -9
- 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/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 +47 -6
- package/lib/types/scope.d.ts +10 -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): 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,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;
|
package/lib/types/registry.d.ts
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* only when the operator removes it (or when the store is repaired). Storage
|
|
23
23
|
* that cannot be parsed at all is backed up and skipped by the domain layer,
|
|
24
24
|
* with a warning.
|
|
25
|
-
* - **Mutations are serialized
|
|
26
|
-
* whole read → validate → persist sequence inside
|
|
25
|
+
* - **Mutations and fs admissions are serialized store-wide.** Every mutation
|
|
26
|
+
* runs its whole read → validate → persist sequence inside the store queue. The
|
|
27
27
|
* storage domain serializes individual `put()`/`delete()` calls, which is not
|
|
28
28
|
* enough: two concurrent `add()` calls would both read the same snapshot and
|
|
29
29
|
* the later write would drop the earlier one, and a removal racing an add
|
|
@@ -45,7 +45,10 @@
|
|
|
45
45
|
*/
|
|
46
46
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
47
47
|
import type { SandboxExecutionPolicy } from '@deepseek-ai/dsh-sandbox';
|
|
48
|
+
import type { RegistryOperation, RegistryOperationKind } from './registry-operation.ts';
|
|
48
49
|
import { type AdditionalRootId, type RegisteredRoot, type RootAccess, type RootRef, type RootStatus } from './roots.ts';
|
|
50
|
+
/** Operator/test capability diagnostics; never bypasses peer authentication. */
|
|
51
|
+
export { nativeHostAvailability } from './authority/native.ts';
|
|
49
52
|
/** The domain name; also the store file stem (`$DSH_HOME/storages/<name>.json`). */
|
|
50
53
|
export declare const DOMAIN_NAME = "multi_root_workspace";
|
|
51
54
|
/** The single table name inside the domain. */
|
|
@@ -76,6 +79,9 @@ export type RegistryAuthorityState = {
|
|
|
76
79
|
readonly kind: 'active';
|
|
77
80
|
} | {
|
|
78
81
|
readonly kind: 'contended';
|
|
82
|
+
} | {
|
|
83
|
+
readonly kind: 'follower';
|
|
84
|
+
readonly epoch: string;
|
|
79
85
|
} | {
|
|
80
86
|
readonly kind: 'storage-failed';
|
|
81
87
|
readonly reason: string;
|
|
@@ -92,6 +98,8 @@ export type RegistryAuthorityState = {
|
|
|
92
98
|
export interface Config {
|
|
93
99
|
/** Absolute path of the store-wide kernel lock file. */
|
|
94
100
|
readonly leasePath?: string;
|
|
101
|
+
/** Explicit medium binding for the multi-Host authority service. */
|
|
102
|
+
readonly sharedStorageRoot?: string;
|
|
95
103
|
/**
|
|
96
104
|
* Cap on STORED additional-root records per primary root (every state
|
|
97
105
|
* counts, including withheld and unusable ones, so the count is stable and
|
|
@@ -191,6 +199,16 @@ export declare class MultiRootRegistry extends Service {
|
|
|
191
199
|
static inject: string[];
|
|
192
200
|
/** Absolute lock path; absent means the row was composed without `leasePath`. */
|
|
193
201
|
private readonly leasePath;
|
|
202
|
+
private readonly sharedStorageRoot;
|
|
203
|
+
private readonly authorityStorageContext;
|
|
204
|
+
private medium;
|
|
205
|
+
private server;
|
|
206
|
+
private client;
|
|
207
|
+
private connectionFailure;
|
|
208
|
+
private connectionGeneration;
|
|
209
|
+
private remoteProjectionReady;
|
|
210
|
+
private readonly remoteRevisions;
|
|
211
|
+
private readonly operationProof;
|
|
194
212
|
/** Whether this process currently owns the store. */
|
|
195
213
|
private authorityState;
|
|
196
214
|
/** Held kernel lease; present once acquire succeeded, including after a failed open. */
|
|
@@ -203,8 +221,12 @@ export declare class MultiRootRegistry extends Service {
|
|
|
203
221
|
private readonly cache;
|
|
204
222
|
/** Signature of the last published grant per key, so a no-op refresh stays a no-op. */
|
|
205
223
|
private readonly published;
|
|
206
|
-
/**
|
|
224
|
+
/** Pending store-ordered work, grouped by primary for teardown accounting. */
|
|
207
225
|
private readonly chains;
|
|
226
|
+
/** Admissions and mutations share one order, including across primary roots. */
|
|
227
|
+
private operationChain;
|
|
228
|
+
private operationEpoch;
|
|
229
|
+
private operationRevision;
|
|
208
230
|
/** Store-wide queue for lease acquire / open / release. */
|
|
209
231
|
private authorityChain;
|
|
210
232
|
/** Change listeners, keyed by their own disposer identity. */
|
|
@@ -245,6 +267,9 @@ export declare class MultiRootRegistry extends Service {
|
|
|
245
267
|
* Whether this process is the Registry Authority for the store-wide medium.
|
|
246
268
|
* Tests and diagnostics read this; surfaces use {@link unavailable}.
|
|
247
269
|
*/
|
|
270
|
+
get epoch(): string | undefined;
|
|
271
|
+
get shared(): boolean;
|
|
272
|
+
get available(): boolean;
|
|
248
273
|
get authority(): RegistryAuthorityState;
|
|
249
274
|
/**
|
|
250
275
|
* The configured cap on stored additional-root records per primary root.
|
|
@@ -289,6 +314,13 @@ export declare class MultiRootRegistry extends Service {
|
|
|
289
314
|
* @returns the refreshed status list.
|
|
290
315
|
*/
|
|
291
316
|
refresh(primaryRoot: string): Promise<readonly RootStatus[]>;
|
|
317
|
+
/**
|
|
318
|
+
* Admit one fs invocation in the same order as mutations, then run its I/O
|
|
319
|
+
* OUTSIDE the queue. Revocation does not wait for already-admitted work.
|
|
320
|
+
* Shared followers obtain the same ordered snapshot through attested IPC.
|
|
321
|
+
*/
|
|
322
|
+
withOperation<T>(policy: SandboxExecutionPolicy, kind: RegistryOperationKind, body: (operation: RegistryOperation) => T | Promise<T>): Promise<T>;
|
|
323
|
+
withPrimaryOperation<T>(policy: SandboxExecutionPolicy, body: () => T): T;
|
|
292
324
|
/**
|
|
293
325
|
* Registers one additional root.
|
|
294
326
|
* @param primaryRoot - the session workspace root the root belongs to.
|
|
@@ -419,8 +451,8 @@ export declare class MultiRootRegistry extends Service {
|
|
|
419
451
|
*/
|
|
420
452
|
private persist;
|
|
421
453
|
/**
|
|
422
|
-
* Run one job
|
|
423
|
-
*
|
|
454
|
+
* Run one job after every store job already queued. The whole
|
|
455
|
+
* read → validate → write sequence belongs inside the job:
|
|
424
456
|
* the storage domain serializes individual writes, so without this the loser
|
|
425
457
|
* of a race reads a snapshot that is already stale and writes it back.
|
|
426
458
|
*/
|
|
@@ -446,6 +478,15 @@ export declare class MultiRootRegistry extends Service {
|
|
|
446
478
|
*/
|
|
447
479
|
private readonly metadataPublished;
|
|
448
480
|
private publish;
|
|
481
|
+
private projectRoots;
|
|
482
|
+
private snapshotOf;
|
|
483
|
+
private captureSnapshot;
|
|
484
|
+
private notifyShared;
|
|
485
|
+
private applySnapshot;
|
|
486
|
+
private remoteUnavailable;
|
|
487
|
+
private remoteSnapshot;
|
|
488
|
+
private remote;
|
|
489
|
+
private dispatchShared;
|
|
449
490
|
/**
|
|
450
491
|
* The open table.
|
|
451
492
|
* @returns the live table.
|
|
@@ -494,7 +535,7 @@ export declare class MultiRootRegistry extends Service {
|
|
|
494
535
|
*/
|
|
495
536
|
private releaseAuthority;
|
|
496
537
|
/**
|
|
497
|
-
* Wait until no
|
|
538
|
+
* Wait until no store work is queued or running (operation I/O is outside).
|
|
498
539
|
*
|
|
499
540
|
* One pass is enough, and only because the authority state has already been
|
|
500
541
|
* flipped: no further mutation can be admitted (it would fail in
|
package/lib/types/scope.d.ts
CHANGED
|
@@ -208,6 +208,7 @@ export declare class MultiRootScopeService extends Service {
|
|
|
208
208
|
/** Additional roots keyed by canonical primary root; empty until roots are registered. */
|
|
209
209
|
private readonly rootsByPrimary;
|
|
210
210
|
private readonly listeners;
|
|
211
|
+
private admissionProof;
|
|
211
212
|
/**
|
|
212
213
|
* The registry-authority fact the registry row publishes. Defaults to
|
|
213
214
|
* `available`: a composition without a registry row can never have
|
|
@@ -244,6 +245,15 @@ export declare class MultiRootScopeService extends Service {
|
|
|
244
245
|
* @param policy - the upstream policy whose workspace root is the primary root.
|
|
245
246
|
* @returns the canonical scope; `additionalRoots` is empty when none are registered.
|
|
246
247
|
*/
|
|
248
|
+
requireAdmission(proof: object): void;
|
|
249
|
+
rememberProfile(argv: readonly string[]): void;
|
|
250
|
+
permitsProfile(argv: readonly string[]): boolean;
|
|
251
|
+
get admissionRequired(): boolean;
|
|
252
|
+
admissionPathsValid(): boolean;
|
|
253
|
+
hasAdmission(): boolean;
|
|
254
|
+
/** Trusted display/review projection, never an execution permit. */
|
|
255
|
+
snapshot(policy: SandboxExecutionPolicy): FilesystemScope;
|
|
256
|
+
resolveSnapshot(policy: SandboxExecutionPolicy, roots: readonly AdditionalWorkspaceRoot[], available: boolean): FilesystemScope;
|
|
247
257
|
resolve(policy: SandboxExecutionPolicy): FilesystemScope;
|
|
248
258
|
/** Read-only pending-dispatch projection. Does not mint an ALS execution grant. */
|
|
249
259
|
previewForTool(policy: SandboxExecutionPolicy, toolName: string): FilesystemScope;
|
|
@@ -7,6 +7,7 @@ export declare const SKILLS_PROVIDER = "multi-root-workspace";
|
|
|
7
7
|
export declare function internalSkillName(primary: string, rootId: string, skillName: string): string;
|
|
8
8
|
export interface LocatedSkill {
|
|
9
9
|
readonly primary: string;
|
|
10
|
+
readonly authorityEpoch?: string | undefined;
|
|
10
11
|
readonly rootId: string;
|
|
11
12
|
readonly aliasId: string;
|
|
12
13
|
readonly candidate: SkillCandidate;
|
|
@@ -27,8 +28,10 @@ export declare class AdditionalSkillCatalog {
|
|
|
27
28
|
current(primary: string, id: string): RootStatus | undefined;
|
|
28
29
|
private withReader;
|
|
29
30
|
list(primary: string, options: SkillLookupOptions): Promise<SkillCandidate[]>;
|
|
31
|
+
private listAdmitted;
|
|
30
32
|
/** Locators are created here and are not accepted from a client path or object. */
|
|
31
33
|
load(primary: string, entry: SkillCandidate, options: SkillLookupOptions, user: boolean): Promise<SkillDefinition | undefined>;
|
|
34
|
+
private loadAdmitted;
|
|
32
35
|
admissionValid(definition: SkillDefinition): Promise<boolean>;
|
|
33
36
|
dispose(): Promise<void>;
|
|
34
37
|
}
|