@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.
Files changed (52) hide show
  1. package/CHANGELOG.en.md +25 -1
  2. package/CHANGELOG.md +25 -1
  3. package/README.en.md +10 -8
  4. package/README.md +10 -8
  5. package/cordis.patch.yml +7 -2
  6. package/lib/auto-review.js +1 -1
  7. package/lib/client.js +197 -74
  8. package/lib/client.js.map +1 -1
  9. package/lib/{command-BGdz7VBH.js → command-B9855H7y.js} +3 -2
  10. package/lib/command.js +1 -1
  11. package/lib/{compat-BiW-m_q0.js → compat-Bg2HFVMX.js} +3 -0
  12. package/lib/compat.js +1 -1
  13. package/lib/{sandbox-CXk5h-ct.js → dialects-Dr4rDR-H.js} +6 -264
  14. package/lib/{fs-BTXySwSG.js → fs-CfzJq2wd.js} +23 -8
  15. package/lib/fs.js +1 -1
  16. package/lib/index.js +7 -6
  17. package/lib/{instructions-MePoRigy.js → instructions-KsUwUFE0.js} +53 -37
  18. package/lib/instructions.js +1 -1
  19. package/lib/lsp.js +19 -16
  20. package/lib/native-win32-2waCRkPI.js +312 -0
  21. package/lib/process-fence.js +62 -12
  22. package/lib/protocol-CJOvAvOS.js +652 -0
  23. package/lib/{registry-KSsmU393.js → registry-Bv431TfO.js} +717 -74
  24. package/lib/registry-operation-C2ZV3Jhw.js +116 -0
  25. package/lib/registry.js +3 -2
  26. package/lib/{review-context-AH-SXHmj.js → review-context-NDpKeMCc.js} +1 -1
  27. package/lib/review-context.js +1 -1
  28. package/lib/sandbox-C-vCJCp3.js +278 -0
  29. package/lib/sandbox.js +1 -1
  30. package/lib/scope.js +101 -11
  31. package/lib/skills.js +48 -29
  32. package/lib/types/authority/native-win32.d.ts +7 -0
  33. package/lib/types/authority/native.d.ts +23 -0
  34. package/lib/types/authority/protocol.d.ts +70 -0
  35. package/lib/types/authority/recovery.d.ts +58 -0
  36. package/lib/types/client/RootSwitcher.d.ts +6 -3
  37. package/lib/types/client/files-tree/adapter.d.ts +63 -1
  38. package/lib/types/compat/authority-storage.d.ts +6 -0
  39. package/lib/types/compat/dsh-version.d.ts +4 -4
  40. package/lib/types/compat/open-in-app-device.d.ts +1 -1
  41. package/lib/types/compat/process-entrypoints.d.ts +2 -0
  42. package/lib/types/compat/process-profile.d.ts +3 -0
  43. package/lib/types/compat/sandbox-confine.d.ts +2 -0
  44. package/lib/types/fs.d.ts +4 -1
  45. package/lib/types/instructions.d.ts +0 -47
  46. package/lib/types/registry-operation.d.ts +31 -0
  47. package/lib/types/registry.d.ts +145 -6
  48. package/lib/types/scope.d.ts +28 -0
  49. package/lib/types/skills/catalog.d.ts +3 -0
  50. package/lib/types/skills/pool.d.ts +1 -1
  51. package/lib/workspace-files.js +63 -7
  52. package/package.json +38 -31
@@ -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 per primary root.** Every mutation runs its
26
- * whole read → validate → persist sequence inside one queue per key. The
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. */
@@ -67,6 +70,28 @@ export declare const REGISTRY_CONTENDED_MESSAGE = "the root registry is owned by
67
70
  export declare const DEFAULT_MAX_ADDITIONAL_ROOTS = 16;
68
71
  /** Hard ceiling for `maxAdditionalRoots`: values above it are config-invalid. */
69
72
  export declare const MAX_ADDITIONAL_ROOTS_CEILING = 64;
73
+ /**
74
+ * The recovery class of a `storage-failed` authority: `retryable` failures
75
+ * (open/listen/acquire) may be re-attempted by `ensureAuthority`, while
76
+ * `config` (a row the operator must fix) and `quarantine` (an unknown durable
77
+ * outcome, isolated until restart) make the state terminal.
78
+ */
79
+ export type StorageFailureRecovery = 'retryable' | 'config' | 'quarantine';
80
+ /** The recovery class a degraded authority belongs to (see {@link StorageFailureRecovery}). */
81
+ export type RecoveryClass = 'retryable' | 'config' | 'quarantine';
82
+ /** Operator-facing read-only picture of the registry authority and its recovery. */
83
+ export interface RegistryDiagnostics {
84
+ readonly authorityState: RegistryAuthorityState;
85
+ readonly epoch: string | undefined;
86
+ readonly connectionFailure: string | undefined;
87
+ readonly recoveryClass: RecoveryClass | undefined;
88
+ readonly recovery: {
89
+ readonly phase: 'idle' | 'waiting' | 'attempting' | 'disabled' | 'requires-restart';
90
+ readonly attempts: number;
91
+ readonly nextRetryAt: number | undefined;
92
+ readonly pendingSync: number;
93
+ };
94
+ }
70
95
  /**
71
96
  * Whether this process is the Registry Authority for the store-wide domain
72
97
  * medium. Contended and storage-failed are both fail-closed: the scope stays
@@ -76,9 +101,13 @@ export type RegistryAuthorityState = {
76
101
  readonly kind: 'active';
77
102
  } | {
78
103
  readonly kind: 'contended';
104
+ } | {
105
+ readonly kind: 'follower';
106
+ readonly epoch: string;
79
107
  } | {
80
108
  readonly kind: 'storage-failed';
81
109
  readonly reason: string;
110
+ readonly recovery: StorageFailureRecovery;
82
111
  } | {
83
112
  readonly kind: 'config-invalid';
84
113
  readonly reason: string;
@@ -92,6 +121,8 @@ export type RegistryAuthorityState = {
92
121
  export interface Config {
93
122
  /** Absolute path of the store-wide kernel lock file. */
94
123
  readonly leasePath?: string;
124
+ /** Explicit medium binding for the multi-Host authority service. */
125
+ readonly sharedStorageRoot?: string;
95
126
  /**
96
127
  * Cap on STORED additional-root records per primary root (every state
97
128
  * counts, including withheld and unusable ones, so the count is stable and
@@ -102,6 +133,12 @@ export interface Config {
102
133
  * exactly what classification grants — only new adds are refused.
103
134
  */
104
135
  readonly maxAdditionalRoots?: number;
136
+ /**
137
+ * Whether the registry retries a degraded authority in the background
138
+ * (bounded backoff, `unref`ed). Defaults to `true`; `false` leaves
139
+ * recovery to on-demand calls (refresh, the panel Retry button).
140
+ */
141
+ readonly backgroundRecovery?: boolean;
105
142
  }
106
143
  declare module '@deepseek-ai/cordis' {
107
144
  interface Context {
@@ -191,20 +228,46 @@ export declare class MultiRootRegistry extends Service {
191
228
  static inject: string[];
192
229
  /** Absolute lock path; absent means the row was composed without `leasePath`. */
193
230
  private readonly leasePath;
231
+ private readonly sharedStorageRoot;
232
+ private readonly authorityStorageContext;
233
+ private medium;
234
+ private server;
235
+ private client;
236
+ private connectionFailure;
237
+ private connectionGeneration;
238
+ /** Local cancel of the in-flight handshake; teardown must not wait out hello. */
239
+ private connectionAbort;
240
+ private remoteProjectionReady;
241
+ private readonly remoteRevisions;
242
+ /**
243
+ * Primary roots whose projection must still be synced after a follower
244
+ * reconnect — populated from the tracked cache keys at adoption, removed as
245
+ * each `applySnapshot` lands. Recovery completes only when it empties.
246
+ */
247
+ private pendingSync;
248
+ /** Bounded background retry for degraded authority states (ADR-0026 R1). */
249
+ private readonly recovery;
250
+ private readonly operationProof;
194
251
  /** Whether this process currently owns the store. */
195
252
  private authorityState;
196
253
  /** Held kernel lease; present once acquire succeeded, including after a failed open. */
197
254
  private lease;
198
255
  /** Open domain handle; present only while {@link authority} is `active`. */
199
256
  private domain;
257
+ /** The last failed `openDomain` reason; a repeat failure is not re-logged. */
258
+ private lastOpenFailure;
200
259
  /** The open table; present once this process is the authority. */
201
260
  private table;
202
261
  /** Last classified status list per canonical primary root. */
203
262
  private readonly cache;
204
263
  /** Signature of the last published grant per key, so a no-op refresh stays a no-op. */
205
264
  private readonly published;
206
- /** One promise chain per canonical primary root: mutations never interleave. */
265
+ /** Pending store-ordered work, grouped by primary for teardown accounting. */
207
266
  private readonly chains;
267
+ /** Admissions and mutations share one order, including across primary roots. */
268
+ private operationChain;
269
+ private operationEpoch;
270
+ private operationRevision;
208
271
  /** Store-wide queue for lease acquire / open / release. */
209
272
  private authorityChain;
210
273
  /** Change listeners, keyed by their own disposer identity. */
@@ -245,7 +308,27 @@ export declare class MultiRootRegistry extends Service {
245
308
  * Whether this process is the Registry Authority for the store-wide medium.
246
309
  * Tests and diagnostics read this; surfaces use {@link unavailable}.
247
310
  */
311
+ get epoch(): string | undefined;
312
+ get shared(): boolean;
313
+ get available(): boolean;
248
314
  get authority(): RegistryAuthorityState;
315
+ /** The class a degraded authority belongs to; `undefined` when healthy. */
316
+ private get recoveryClass();
317
+ /**
318
+ * Read-only diagnostics for operators: the authority state, the epoch, the
319
+ * last connection failure, and what the recovery scheduler is doing.
320
+ * `requires-restart` marks classes no timer will ever fix — a `config`
321
+ * row or a `quarantine`d store must NOT read as "retrying". Purely a read;
322
+ * it never arms or runs anything.
323
+ */
324
+ get diagnostics(): RegistryDiagnostics;
325
+ /** The last logged `(kind, recoveryClass)` pair; dedupes transition logs. */
326
+ private loggedRecovery;
327
+ /**
328
+ * Log one line per authority/recovery-class TRANSITION — never per tick:
329
+ * waiting↔attempting moves say nothing an operator did not already know.
330
+ */
331
+ private noteRecoveryTransition;
249
332
  /**
250
333
  * The configured cap on stored additional-root records per primary root.
251
334
  * Surfaces show it next to the count so "N of LIMIT" is always explainable.
@@ -289,6 +372,13 @@ export declare class MultiRootRegistry extends Service {
289
372
  * @returns the refreshed status list.
290
373
  */
291
374
  refresh(primaryRoot: string): Promise<readonly RootStatus[]>;
375
+ /**
376
+ * Admit one fs invocation in the same order as mutations, then run its I/O
377
+ * OUTSIDE the queue. Revocation does not wait for already-admitted work.
378
+ * Shared followers obtain the same ordered snapshot through attested IPC.
379
+ */
380
+ withOperation<T>(policy: SandboxExecutionPolicy, kind: RegistryOperationKind, body: (operation: RegistryOperation) => T | Promise<T>): Promise<T>;
381
+ withPrimaryOperation<T>(policy: SandboxExecutionPolicy, body: () => T): T;
292
382
  /**
293
383
  * Registers one additional root.
294
384
  * @param primaryRoot - the session workspace root the root belongs to.
@@ -391,6 +481,12 @@ export declare class MultiRootRegistry extends Service {
391
481
  grantedFor(policy: SandboxExecutionPolicy): readonly string[];
392
482
  /** Classify one stored record set and cache it. */
393
483
  private accept;
484
+ /**
485
+ * The pure half of {@link accept}: classification plus the over-limit
486
+ * warning, without writing the cache or the scope. {@link openDomain}
487
+ * staging uses it so a half-opened domain cannot materialize rows.
488
+ */
489
+ private classify;
394
490
  /**
395
491
  * The statuses of one key, from memory where possible: the cache, else what
396
492
  * storage holds, else empty. A cached answer is never re-checked here — that
@@ -419,8 +515,8 @@ export declare class MultiRootRegistry extends Service {
419
515
  */
420
516
  private persist;
421
517
  /**
422
- * Run one job for one primary root after every job already queued for that
423
- * root. The whole read → validate → write sequence belongs inside the job:
518
+ * Run one job after every store job already queued. The whole
519
+ * read → validate → write sequence belongs inside the job:
424
520
  * the storage domain serializes individual writes, so without this the loser
425
521
  * of a race reads a snapshot that is already stale and writes it back.
426
522
  */
@@ -446,6 +542,49 @@ export declare class MultiRootRegistry extends Service {
446
542
  */
447
543
  private readonly metadataPublished;
448
544
  private publish;
545
+ private projectRoots;
546
+ private snapshotOf;
547
+ private captureSnapshot;
548
+ private notifyShared;
549
+ /** Depth of {@link commit}; nested calls share the outer round's buffers. */
550
+ private commitDepth;
551
+ /** Keys whose publish inside a commit produced deferred notifications, first-seen order. */
552
+ private readonly commitNotifications;
553
+ /**
554
+ * Run `body` as one Registry commit: scope writes inside it go through
555
+ * `multiRootScope.batch`, and the `notifyShared` broadcasts plus Registry
556
+ * listener calls `publish()` produces are collected per key instead of
557
+ * delivered. When the OUTERMOST commit ends — also when `body` throws — the
558
+ * scope batch flushes first, then the broadcasts go out in first-seen key
559
+ * order, then the listeners: no synchronous callback can observe a
560
+ * half-committed state.
561
+ */
562
+ private commit;
563
+ /** Deliver one key's notifications now, or collect them inside a commit. */
564
+ private deliverNotifications;
565
+ private applySnapshot;
566
+ private remoteUnavailable;
567
+ /**
568
+ * Resynchronize each still-pending tracked primary outside the authority
569
+ * queue: per-key snapshots through the live client. A failed key stays in
570
+ * `pendingSync` for the next tick; a lost connection abandons the round —
571
+ * `remoteUnavailable` already emptied the set.
572
+ */
573
+ private syncProjection;
574
+ /**
575
+ * Write one key's withheld-only projection into the scope, keeping
576
+ * `published` truthful: the signature records what the scope HOLDS, never
577
+ * what `publish()` last computed — a withheld signature can never equal a
578
+ * publish signature, so an empty snapshot afterwards really writes the
579
+ * retraction. A repeated write of the same withheld projection is a no-op
580
+ * (the same-signature skip is also the republish gate of a repeated
581
+ * downgrade). `metadataPublished` is untouched: a withheld write announces
582
+ * nothing to the Registry view.
583
+ */
584
+ private withholdScope;
585
+ private remoteSnapshot;
586
+ private remote;
587
+ private dispatchShared;
449
588
  /**
450
589
  * The open table.
451
590
  * @returns the live table.
@@ -494,7 +633,7 @@ export declare class MultiRootRegistry extends Service {
494
633
  */
495
634
  private releaseAuthority;
496
635
  /**
497
- * Wait until no per-primary-root mutation is queued or running.
636
+ * Wait until no store work is queued or running (operation I/O is outside).
498
637
  *
499
638
  * One pass is enough, and only because the authority state has already been
500
639
  * flipped: no further mutation can be admitted (it would fail in
@@ -208,6 +208,11 @@ 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
+ /** Enclosing {@link MultiRootScopeService.batch} depth; inner calls nest. */
212
+ private batchDepth;
213
+ /** True when a batched write coalesced its notification into the flush. */
214
+ private batchDirty;
215
+ private admissionProof;
211
216
  /**
212
217
  * The registry-authority fact the registry row publishes. Defaults to
213
218
  * `available`: a composition without a registry row can never have
@@ -230,6 +235,20 @@ export declare class MultiRootScopeService extends Service {
230
235
  private readonly dshHome;
231
236
  /** Observe registry publications; consumers still resolve authority from scopeOf(). */
232
237
  subscribe(listener: () => void): () => void;
238
+ /**
239
+ * Run `body` as one notification round: `setAdditionalRoots()` and
240
+ * `setRegistryAuthority()` apply their state immediately (a same-value
241
+ * authority write still stays silent), but subscriber invocations coalesce
242
+ * into a single flush when the OUTERMOST batch ends — also when `body`
243
+ * throws, because the state already landed. A write a subscriber makes
244
+ * during the flush is not part of the batch and notifies synchronously
245
+ * under the normal rules.
246
+ * @param body - the writes to run as one round.
247
+ * @returns the body's return value.
248
+ */
249
+ batch<T>(body: () => T): T;
250
+ /** Notify subscribers now, or coalesce into the enclosing batch's flush. */
251
+ private notifySubscribers;
233
252
  /**
234
253
  * The sorted names of the tools `toolAccess` caps — read-only for the
235
254
  * prompt contribution. An invalid config is not enumerated (it caps every
@@ -244,6 +263,15 @@ export declare class MultiRootScopeService extends Service {
244
263
  * @param policy - the upstream policy whose workspace root is the primary root.
245
264
  * @returns the canonical scope; `additionalRoots` is empty when none are registered.
246
265
  */
266
+ requireAdmission(proof: object): void;
267
+ rememberProfile(argv: readonly string[]): void;
268
+ permitsProfile(argv: readonly string[]): boolean;
269
+ get admissionRequired(): boolean;
270
+ admissionPathsValid(): boolean;
271
+ hasAdmission(): boolean;
272
+ /** Trusted display/review projection, never an execution permit. */
273
+ snapshot(policy: SandboxExecutionPolicy): FilesystemScope;
274
+ resolveSnapshot(policy: SandboxExecutionPolicy, roots: readonly AdditionalWorkspaceRoot[], available: boolean): FilesystemScope;
247
275
  resolve(policy: SandboxExecutionPolicy): FilesystemScope;
248
276
  /** Read-only pending-dispatch projection. Does not mint an ALS execution grant. */
249
277
  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
  }
@@ -10,7 +10,7 @@ export declare class SkillReaderPool {
10
10
  constructor(invalidate: () => void, capacity?: number, intervalMs?: number);
11
11
  get size(): number;
12
12
  get roots(): readonly string[];
13
- use<T>(key: string, create: (signal: AbortSignal) => Promise<Reader | undefined>, run: (reader: Reader | undefined) => Promise<T>, signal?: AbortSignal): Promise<T>;
13
+ use<T>(root: string, create: (signal: AbortSignal) => Promise<Reader | undefined>, run: (reader: Reader | undefined) => Promise<T>, signal?: AbortSignal, identity?: string): Promise<T>;
14
14
  poll(): Promise<void>;
15
15
  dispose(): Promise<void>;
16
16
  private closeEntry;
@@ -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 { l as isCanonicallyUnder } from "./roots-Ca_W1S9n.js";
3
4
  import { t as routeWorkspacePath } from "./workspace-routing-YPxaQ4V3.js";
4
5
  import { posix, resolve, win32 } from "node:path";
@@ -78,19 +79,35 @@ function apply(ctx) {
78
79
  return original.call(this, routed.request, routed.path, relativePath, signal);
79
80
  })) : void 0;
80
81
  const restoreWatch = adaptPathWatch(service, (originalChanges) => async function* (request, path, signal) {
81
- if (scope.multiRootScope.scopeOf(request.workspaceRoot).length === 0 && registeredRoots(scope, request.workspaceRoot).length === 0) {
82
- yield* originalChanges.call(this, request, path, signal);
82
+ const check = async () => {
83
+ if (await withRegistryRead(scope, request.workspaceRoot, "files", () => routeRead(scope, request, path, signal)) === "deny") throw await outside(path);
84
+ };
85
+ if (scope.get("multiRootRegistry")?.shared) await check();
86
+ if (scope.multiRootScope.snapshot({
87
+ workspaceRoot: request.workspaceRoot,
88
+ mode: "read-only"
89
+ }).additionalRoots.length === 0 && registeredRoots(scope, request.workspaceRoot).length === 0) {
90
+ for await (const frame of originalChanges.call(this, request, path, signal)) {
91
+ if (scope.get("multiRootRegistry")?.shared) await check();
92
+ yield frame;
93
+ }
83
94
  return;
84
95
  }
85
- const route = await routeWorkspacePath(scope, request.workspaceRoot, path, signal);
96
+ const route = await withRegistryRead(scope, request.workspaceRoot, "files", () => routeWorkspacePath(scope, request.workspaceRoot, path, signal));
86
97
  if (route === void 0 || route.workspaceRoot === route.primaryRoot) {
87
98
  if (underWithheldRoot(scope, request.workspaceRoot, path)) throw await outside(path);
88
- yield* originalChanges.call(this, request, path, signal);
99
+ for await (const frame of originalChanges.call(this, request, path, signal)) {
100
+ if (scope.get("multiRootRegistry")?.shared) await check();
101
+ yield frame;
102
+ }
89
103
  return;
90
104
  }
91
105
  const lifetime = new AbortController();
92
106
  const unsubscribe = scope.multiRootScope.subscribe(() => {
93
- if (!scope.multiRootScope.scopeOf(route.primaryRoot).includes(route.workspaceRoot)) lifetime.abort(/* @__PURE__ */ new Error("the additional root was revoked"));
107
+ if (!scope.multiRootScope.snapshot({
108
+ workspaceRoot: route.primaryRoot,
109
+ mode: "read-only"
110
+ }).additionalRoots.includes(route.workspaceRoot)) lifetime.abort(/* @__PURE__ */ new Error("the additional root was revoked"));
94
111
  });
95
112
  try {
96
113
  for await (const frame of originalChanges.call(this, {
@@ -101,14 +118,35 @@ function apply(ctx) {
101
118
  lifetime.signal,
102
119
  rowLifetime.signal
103
120
  ]))) {
104
- if (!scope.multiRootScope.scopeOf(route.primaryRoot).includes(route.workspaceRoot)) throw await outside(path);
121
+ await withRegistryRead(scope, route.primaryRoot, "files", async () => {
122
+ if (!scope.multiRootScope.scopeOf(route.primaryRoot).includes(route.workspaceRoot)) throw await outside(path);
123
+ });
105
124
  yield frame;
106
125
  }
107
126
  } finally {
108
127
  unsubscribe();
109
128
  }
110
129
  });
130
+ const admissionRestores = [];
131
+ {
132
+ const methods = service;
133
+ for (const key of [
134
+ "list",
135
+ "stat",
136
+ "read",
137
+ "readBytes",
138
+ "readAll",
139
+ "readRelated"
140
+ ]) {
141
+ if (typeof methods[key] !== "function") continue;
142
+ admissionRestores.push(wrapMethod(methods, key, (original) => async function(...args) {
143
+ const request = args[0];
144
+ return withRegistryRead(scope, request.workspaceRoot, "files", () => original.apply(this, args));
145
+ }));
146
+ }
147
+ }
111
148
  return () => {
149
+ for (const restore of admissionRestores.reverse()) restore();
112
150
  rowLifetime.abort(/* @__PURE__ */ new Error("multi-root workspace-files routing unloaded"));
113
151
  restoreList();
114
152
  restoreStat();
@@ -134,6 +172,16 @@ function apply(ctx) {
134
172
  * root, genuinely unrelated paths) keeps upstream's default read access.
135
173
  */
136
174
  async function routeRead(scope, request, path, signal) {
175
+ if (scope.get("multiRootRegistry")?.shared && scope.multiRootScope.resolve({
176
+ workspaceRoot: request.workspaceRoot,
177
+ mode: "read-only"
178
+ }).authority === "unavailable") {
179
+ const route = await routeWorkspacePath(scope, request.workspaceRoot, path, signal);
180
+ return route !== void 0 && route.workspaceRoot === route.primaryRoot ? {
181
+ request,
182
+ path: route.absolutePath
183
+ } : "deny";
184
+ }
137
185
  if (scope.multiRootScope.scopeOf(request.workspaceRoot).length === 0 && registeredRoots(scope, request.workspaceRoot).length === 0) return {
138
186
  request,
139
187
  path
@@ -187,12 +235,20 @@ function underWithheldRoot(scope, primaryRoot, path) {
187
235
  * file, so the granted base must not reach back into a dead grant.
188
236
  */
189
237
  async function relatedTailWithheld(scope, primaryRoot, base, tail, signal) {
190
- if (registeredRoots(scope, primaryRoot).length === 0) return void 0;
238
+ const unknown = scope.get("multiRootRegistry")?.shared && scope.multiRootScope.resolve({
239
+ workspaceRoot: primaryRoot,
240
+ mode: "read-only"
241
+ }).authority === "unavailable";
242
+ if (registeredRoots(scope, primaryRoot).length === 0 && !unknown) return void 0;
191
243
  const targetBase = await scope.fs.resolve(base.path, {
192
244
  cwd: base.request.workspaceRoot,
193
245
  signal
194
246
  });
195
247
  const target = resolveRelatedWorkspacePath(scope.fs.processPath(targetBase), tail);
248
+ if (unknown && await routeRead(scope, {
249
+ ...base.request,
250
+ workspaceRoot: primaryRoot
251
+ }, target, signal) === "deny") return target;
196
252
  return underWithheldRoot(scope, primaryRoot, target) ? target : void 0;
197
253
  }
198
254
  async function outside(path) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dsh-electron/dsh-plugin-multi-root-workspace",
3
3
  "description": "Multi-root Workspace for DeepSeek Harness: adds N additional roots to the workspace sandbox scope as an out-of-tree bundle",
4
- "version": "0.1.11",
4
+ "version": "0.1.13",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -106,33 +106,34 @@
106
106
  "license": "MIT",
107
107
  "peerDependencies": {
108
108
  "@deepseek-ai/cordis": ">=4.0.1 <5.0.0 || 4.0.5-alpha.1 || 4.0.5-alpha.2 || 4.0.6-alpha.1",
109
- "@deepseek-ai/dsh-agent": "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",
110
- "@deepseek-ai/dsh-agent-instructions": "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",
111
- "@deepseek-ai/dsh-api-workspace-files": "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",
112
- "@deepseek-ai/dsh-client-connection": "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",
113
- "@deepseek-ai/dsh-commands": "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",
114
- "@deepseek-ai/dsh-fs": "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",
115
- "@deepseek-ai/dsh-fs-local": "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",
116
- "@deepseek-ai/dsh-llm": "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",
117
- "@deepseek-ai/dsh-lsp": "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",
118
- "@deepseek-ai/dsh-lsp-stdio": "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",
119
- "@deepseek-ai/dsh-sandbox": "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",
120
- "@deepseek-ai/dsh-sandbox-local": "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",
121
- "@deepseek-ai/dsh-sandbox-policy": "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",
122
- "@deepseek-ai/dsh-sandbox-windows-acl": "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",
123
- "@deepseek-ai/dsh-session": "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",
124
- "@deepseek-ai/dsh-storage-domain": "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",
125
- "@deepseek-ai/dsh-subprocess": "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",
126
- "@deepseek-ai/dsh-system-prompt": "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",
127
- "@deepseek-ai/dsh-tools": "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",
128
- "@deepseek-ai/dsh-typert-protocol": "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",
129
- "@deepseek-ai/dsh-permission-presets": "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",
130
- "@deepseek-ai/dsh-util-values": "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",
131
- "@deepseek-ai/dsh-skill": "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",
132
- "@deepseek-ai/dsh-skill-filesystem": "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",
133
- "@deepseek-ai/dsh-settings": "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",
134
- "@deepseek-ai/dsh-api-session-controller": "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",
135
- "@deepseek-ai/dsh-session-query": "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"
109
+ "@deepseek-ai/dsh-agent": "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",
110
+ "@deepseek-ai/dsh-agent-instructions": "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",
111
+ "@deepseek-ai/dsh-api-workspace-files": "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",
112
+ "@deepseek-ai/dsh-client-connection": "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",
113
+ "@deepseek-ai/dsh-commands": "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",
114
+ "@deepseek-ai/dsh-fs": "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",
115
+ "@deepseek-ai/dsh-fs-local": "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",
116
+ "@deepseek-ai/dsh-llm": "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",
117
+ "@deepseek-ai/dsh-lsp": "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",
118
+ "@deepseek-ai/dsh-lsp-stdio": "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",
119
+ "@deepseek-ai/dsh-sandbox": "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",
120
+ "@deepseek-ai/dsh-sandbox-local": "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",
121
+ "@deepseek-ai/dsh-sandbox-policy": "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",
122
+ "@deepseek-ai/dsh-sandbox-windows-acl": "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",
123
+ "@deepseek-ai/dsh-session": "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",
124
+ "@deepseek-ai/dsh-storage-domain": "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",
125
+ "@deepseek-ai/dsh-subprocess": "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",
126
+ "@deepseek-ai/dsh-system-prompt": "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",
127
+ "@deepseek-ai/dsh-tools": "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",
128
+ "@deepseek-ai/dsh-typert-protocol": "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",
129
+ "@deepseek-ai/dsh-permission-presets": "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",
130
+ "@deepseek-ai/dsh-util-values": "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",
131
+ "@deepseek-ai/dsh-skill": "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",
132
+ "@deepseek-ai/dsh-skill-filesystem": "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",
133
+ "@deepseek-ai/dsh-settings": "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",
134
+ "@deepseek-ai/dsh-api-session-controller": "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",
135
+ "@deepseek-ai/dsh-session-query": "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",
136
+ "@deepseek-ai/dsh-storage-json": "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"
136
137
  },
137
138
  "peerDependenciesMeta": {
138
139
  "@deepseek-ai/dsh-agent": {
@@ -197,6 +198,9 @@
197
198
  },
198
199
  "@deepseek-ai/dsh-session-query": {
199
200
  "optional": true
201
+ },
202
+ "@deepseek-ai/dsh-storage-json": {
203
+ "optional": true
200
204
  }
201
205
  },
202
206
  "devDependencies": {
@@ -243,6 +247,7 @@
243
247
  "@deepseek-ai/dsh-util-workspace-path": "0.2.2-alpha.2",
244
248
  "@deepseek-ai/dsh-workspace": "0.2.2-alpha.2",
245
249
  "@testing-library/react": "^16.3.3",
250
+ "@modelcontextprotocol/sdk": "^1.32.2",
246
251
  "@types/node": "^22.0.0",
247
252
  "@types/react": "~18.3.31",
248
253
  "@types/react-dom": "~18.3.7",
@@ -254,7 +259,7 @@
254
259
  "react-dom": "^18.3.1",
255
260
  "tsdown": "^0.22.0",
256
261
  "typescript": "^6.0.3",
257
- "vitest": "^4.1.8",
262
+ "vitest": "^4.1.12",
258
263
  "yaml": "^2.9.0",
259
264
  "zustand": "~4.5.7",
260
265
  "@deepseek-ai/dsh-skill": "0.2.2-alpha.2",
@@ -281,7 +286,7 @@
281
286
  "dependencies": {
282
287
  "@deepseek-ai/node-addon-system": "^0.1.2",
283
288
  "koffi": "^3.1.0",
284
- "zod": "^4.6.2",
289
+ "zod": "^4.6.7",
285
290
  "@deepseek-ai/schemastery": "^3.18.2"
286
291
  },
287
292
  "scripts": {
@@ -305,6 +310,8 @@
305
310
  "typecheck": "tsc -p tsconfig.host.json --noEmit && tsc -p tsconfig.client.json --noEmit && node scripts/build-auto-review.mjs",
306
311
  "verify:all": "pnpm lint && pnpm typecheck && pnpm build && pnpm kernel:probe && pnpm test && pnpm smoke && pnpm pack:check && pnpm smoke:packed && pnpm auto-review:fork && pnpm smoke:auto-review",
307
312
  "auto-review:fork": "node scripts/check-auto-review-fork.mjs",
308
- "smoke:auto-review": "node scripts/verify-auto-review.mjs"
313
+ "smoke:auto-review": "node scripts/verify-auto-review.mjs",
314
+ "registry:probe": "node scripts/check-registry-authority.mjs",
315
+ "soak:tui-registry": "node scripts/observe-tui-registry.mjs"
309
316
  }
310
317
  }