@dsh-electron/dsh-plugin-multi-root-workspace 0.1.15 → 0.1.16

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 (39) hide show
  1. package/CHANGELOG.en.md +13 -1
  2. package/CHANGELOG.md +13 -1
  3. package/README.en.md +6 -6
  4. package/README.md +6 -6
  5. package/lib/auto-review.js +1 -1
  6. package/lib/client.js +761 -74
  7. package/lib/client.js.map +1 -1
  8. package/lib/{command-y5tfcAeC.js → command-DaVJ-mK3.js} +55 -1
  9. package/lib/command.js +1 -1
  10. package/lib/index.js +4 -4
  11. package/lib/{instructions-Ck9wp4J4.js → instructions-Ca80SkZq.js} +1 -1
  12. package/lib/instructions.js +1 -1
  13. package/lib/lsp.js +1 -1
  14. package/lib/process-fence.js +95 -12
  15. package/lib/{registry-DyDNDSJZ.js → registry-BibiukWk.js} +1 -1
  16. package/lib/{registry-operation-C2ZV3Jhw.js → registry-operation-CIlmt6_c.js} +5 -1
  17. package/lib/registry.js +1 -1
  18. package/lib/{review-context-B-oHkU6a.js → review-context-OxO-fMWR.js} +1 -1
  19. package/lib/review-context.js +1 -1
  20. package/lib/{sandbox-CZElTNPz.js → sandbox-aobdyQGi.js} +6 -3
  21. package/lib/sandbox.js +1 -1
  22. package/lib/scope.js +91 -4
  23. package/lib/skills.js +1 -1
  24. package/lib/types/client/files-tree/FilesTree.d.ts +49 -6
  25. package/lib/types/client/files-tree/adapter.d.ts +69 -16
  26. package/lib/types/client/files-tree/face.d.ts +31 -2
  27. package/lib/types/client/files-tree/locate.d.ts +74 -0
  28. package/lib/types/client/files-tree/navigator.d.ts +98 -0
  29. package/lib/types/client/index.d.ts +7 -0
  30. package/lib/types/client/locales.d.ts +2 -0
  31. package/lib/types/client/styles.d.ts +1 -1
  32. package/lib/types/compat/files-tree.d.ts +8 -1
  33. package/lib/types/compat/forge-entrypoints.d.ts +44 -0
  34. package/lib/types/compat/session-existence.d.ts +41 -0
  35. package/lib/types/registry-operation.d.ts +7 -0
  36. package/lib/types/roots.d.ts +3 -1
  37. package/lib/types/scope.d.ts +67 -1
  38. package/lib/workspace-files.js +1 -1
  39. package/package.json +1 -1
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Compatibility adapter: which foreign-service entries the process fence can
3
+ * admit.
4
+ *
5
+ * The upstream experimental forge bundle composes `ctx.sandbox.confine` +
6
+ * `ctx.subprocess.spawn` on its own code paths — the panel's Git reads and the
7
+ * worktree service's creation/restore commands — bypassing every wrapped
8
+ * executor seam the admission already reaches. The fence therefore wraps each
9
+ * reachable async public entry with an ordered admission of its own. Which
10
+ * services and keys exist is a per-release shape: the verified baseline here
11
+ * is `@deepseek-ai/dsh-experimental-forge-bundle@0.2.2-rc.1` (which pins
12
+ * `dsh-experimental-worktree` and `dsh-experimental-client-ui-forge` at the
13
+ * same version), and the `follow`/`changeRequestStates` async generators are
14
+ * deliberately absent — a generator cannot hold a synchronous admission, so
15
+ * its detached work falls back to the minted-profile ledger.
16
+ *
17
+ * @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/forge-entrypoints
18
+ */
19
+ import type { Session } from '@deepseek-ai/dsh-session';
20
+ /** One foreign service whose public entries spawn confined processes. */
21
+ export interface ForeignProcessService {
22
+ readonly service: string;
23
+ readonly keys: readonly string[];
24
+ }
25
+ /**
26
+ * The forge-bundle service entries an admission can wrap, in this order.
27
+ * `worktrees.create`/`restore` take an `Agent` (the session rides on
28
+ * `agent.session`); every listed `forgePanel` entry takes the `Session` as
29
+ * its first argument. Entries not listed — background inspections, the
30
+ * isolated `forge-worktree` instance inside the panel's mount group — stay
31
+ * ledger-covered.
32
+ */
33
+ export declare const FORGE_PROCESS_SERVICES: readonly ForeignProcessService[];
34
+ /**
35
+ * Read the session a foreign-service entry resolves its policy from — the
36
+ * first argument alone: a `Session`, or an `Agent`-shaped value carrying one.
37
+ * The probe is deliberately structural and narrow (`id` + `header`): it is a
38
+ * trust boundary over the upstream type, not a full validation — anything
39
+ * else runs unadmitted rather than let `sandboxPolicy.resolve` read fields a
40
+ * foreign argument lacks.
41
+ * @param first - the wrapped call's first argument.
42
+ * @returns the session, or `undefined` for a call that does not carry one.
43
+ */
44
+ export declare function sessionOf(first: unknown): Session | undefined;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Compatibility adapter for "does this session exist in durable storage".
3
+ *
4
+ * The panel session anchor (ADR-0008) is a live Session in this process's
5
+ * `SessionStore`. A persisted session owned by another Host — or one still
6
+ * mid-resume — never enters that store, so `resolvePanelPrimaryRoot` and the
7
+ * `rootsViewOf` re-check both answer `session-not-found`. That code is true
8
+ * but diagnostically wrong when the session exists: the panel copy asked the
9
+ * user to open a session already on screen.
10
+ *
11
+ * `sessionPersistence.stat(id)` is the cheapest probe of the durable fact:
12
+ * it reads only the session header — never the event log, never the
13
+ * cross-process write lease — and reports `undefined` for an absent id.
14
+ * This module isolates the probe because the fact's meaning differs by
15
+ * outcome:
16
+ *
17
+ * - snapshot returned → the session exists (writer-held elsewhere, still
18
+ * resuming, or needing a format migration — `stat` reports the header in
19
+ * every one of those states): reclassify as `session-not-live`;
20
+ * - `undefined` → the session does not exist in this backend: keep
21
+ * `session-not-found`;
22
+ * - service or `stat` absent (a composition without persistence, or a
23
+ * runtime whose backend does not name it) → `false`: the probe cannot
24
+ * prove existence, so the caller keeps the code it already had;
25
+ * - `stat` throws → `false`, same conservatism. One imprecision survives:
26
+ * a session that exists but whose header cannot be read (corrupt durable
27
+ * state, a broken backend) is reported as not-found rather than not-live —
28
+ * identical to the pre-probe behavior, and preferable to claiming
29
+ * existence the probe failed to prove.
30
+ *
31
+ * @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/session-existence
32
+ */
33
+ import type { Context } from '@deepseek-ai/cordis';
34
+ /**
35
+ * Whether `sessionId` exists in the mounted persistence backend.
36
+ * @param ctx - the plugin fiber context; optional services read via `ctx.get`.
37
+ * @param sessionId - the panel request's session id.
38
+ * @param signal - the request's cancellation, forwarded when the backend takes options.
39
+ * @returns `true` only when a snapshot proves the session exists.
40
+ */
41
+ export declare function persistedSessionExists(ctx: Context, sessionId: string, signal?: AbortSignal): Promise<boolean>;
@@ -2,6 +2,13 @@ import type { SandboxMode } from '@deepseek-ai/dsh-sandbox';
2
2
  import type { FilesystemScope } from './scope.ts';
3
3
  export type RegistryOperationKind = 'fs-write' | 'fs-edit' | 'process' | 'pty' | 'lsp' | 'files' | 'instructions' | 'skills';
4
4
  export declare function currentRegistryOperation(proof: object): RegistryOperation | undefined;
5
+ /**
6
+ * The enclosing admission frame's mint disposition for this proof. `expired`
7
+ * frames still occupy the async context of their descendants; an expired
8
+ * admission must never bequeath a launchable profile.
9
+ */
10
+ export type OperationFrameState = 'active' | 'expired' | 'absent';
11
+ export declare function operationFrameState(proof: object): OperationFrameState;
5
12
  export interface RegistryOperationIdentity {
6
13
  readonly operationId: string;
7
14
  readonly kind: RegistryOperationKind;
@@ -202,7 +202,9 @@ export type RootRef = {
202
202
  readonly addedAt: string;
203
203
  };
204
204
  /** Every way root handling can fail, as a stable code the surfaces localize. */
205
- export type RootValidationCode = 'not-absolute' | 'missing' | 'not-a-directory' | 'equals-primary' | 'primary-overlap' | 'duplicate' | 'nested' | 'invalid-alias' | 'invalid-alias-id' | 'not-found' | 'invalid-ref' | 'storage-unavailable' | 'registry-contended' | 'root-limit-reached' | 'config-invalid' | 'reveal-unavailable' | 'session-not-found' | 'unsupported-path' | 'root-unavailable' | 'protected-path' | 'protected-boundary-unknown' | 'revive-target-changed';
205
+ export type RootValidationCode = 'not-absolute' | 'missing' | 'not-a-directory' | 'equals-primary' | 'primary-overlap' | 'duplicate' | 'nested' | 'invalid-alias' | 'invalid-alias-id' | 'not-found' | 'invalid-ref' | 'storage-unavailable' | 'registry-contended' | 'root-limit-reached' | 'config-invalid' | 'reveal-unavailable' | 'session-not-found'
206
+ /** Synthesized at the panel dispatch catch — the session existed but could not resolve a root during the request (writer-held, mid-resume, or a race resolved after the throw); no RootValidationError is constructed with it. */
207
+ | 'session-not-live' | 'unsupported-path' | 'root-unavailable' | 'protected-path' | 'protected-boundary-unknown' | 'revive-target-changed';
206
208
  /** A rejected root operation; `code` is the stable contract, `message` the fallback prose. */
207
209
  export declare class RootValidationError extends Error {
208
210
  readonly code: RootValidationCode;
@@ -200,6 +200,24 @@ export interface Config {
200
200
  */
201
201
  readonly dshHome?: string;
202
202
  }
203
+ /**
204
+ * The baseline a mint resolved to. Only primary-bounded forms may join the
205
+ * unadmitted ledger: `clamped` (primary-root clamp) and `read-only` (the
206
+ * upstream read-only skeleton — never writable). `ambient` mints —
207
+ * `restrictedExecution`'s ambient-minus-denies profile, and any mint whose
208
+ * scope could not be proven unavailable — may only live bound to an
209
+ * admission frame, and otherwise die.
210
+ */
211
+ export type MintForm = 'clamped' | 'read-only' | 'ambient';
212
+ /** The authorization basis a mint carried at its synchronous entry, stamped onto its ledger entry. */
213
+ export interface MintStamp {
214
+ /** Publication generation the mint's authorization snapshot was resolved under. */
215
+ readonly version: number;
216
+ /** Tool-cap basis at the same instant. */
217
+ readonly capped: boolean;
218
+ /** The profile form the mint produced; gates ledger admission. */
219
+ readonly form: MintForm;
220
+ }
203
221
  /**
204
222
  * The `ctx.multiRootScope` service. Providers ask it — never the filesystem —
205
223
  * what the current scope is, which is what keeps the fs fence and every kernel
@@ -215,6 +233,14 @@ export declare class MultiRootScopeService extends Service {
215
233
  /** True when a batched write coalesced its notification into the flush. */
216
234
  private batchDirty;
217
235
  private admissionProof;
236
+ /**
237
+ * The publication generation the mint ledger is keyed on: bumped when a
238
+ * root-table write or an authority transition changes the authorization
239
+ * basis (a repeat publish of identical content is not a publication), so a
240
+ * minted profile dies with the state it was computed from.
241
+ */
242
+ private publicationVersion;
243
+ private mintedProfiles;
218
244
  /**
219
245
  * The registry-authority fact the registry row publishes. Defaults to
220
246
  * `available`: a composition without a registry row can never have
@@ -258,6 +284,8 @@ export declare class MultiRootScopeService extends Service {
258
284
  batch<T>(body: () => T): T;
259
285
  /** Notify subscribers now, or coalesce into the enclosing batch's flush. */
260
286
  private notifySubscribers;
287
+ /** A publication superseded the authorization basis: every outstanding minted profile dies. */
288
+ private bumpPublication;
261
289
  /**
262
290
  * The sorted names of the tools `toolAccess` caps — read-only for the
263
291
  * prompt contribution. An invalid config is not enumerated (it caps every
@@ -288,8 +316,46 @@ export declare class MultiRootScopeService extends Service {
288
316
  * @returns the canonical scope; `additionalRoots` is empty when none are registered.
289
317
  */
290
318
  requireAdmission(proof: object): void;
291
- rememberProfile(argv: readonly string[]): void;
319
+ /**
320
+ * The stamp an asynchronously completing mint must carry: the publication
321
+ * generation and tool-cap basis at the mint's synchronous entry, before any
322
+ * provider probe could run. An in-flight mint that crosses a real
323
+ * publication keeps its entry generation, so the ledger refuses it rather
324
+ * than re-stamping stale authorization under the current publication.
325
+ * @param form - the profile form the mint will produce (`clamped` and
326
+ * `read-only` are the only ledger-eligible forms).
327
+ */
328
+ captureMint(form: MintForm): MintStamp;
329
+ /**
330
+ * Record a confined profile this host's provider just minted. Only
331
+ * meaningful under the shared Registry (`admissionProof` set; without it
332
+ * `permitsProfile` never refuses): a live admission binds the profile to
333
+ * the operation and it dies with it; an expired admission never bequeaths
334
+ * one — a mint that outlived its operation is dropped, not demoted into
335
+ * the ledger; only with NO admission frame at all does the profile join
336
+ * the publication-keyed ledger stamped with `mint`, and only when the
337
+ * mint's form is primary-bounded — an `ambient` mint that arrives without
338
+ * a frame is dropped the same way.
339
+ * @param argv - the confined argv just minted.
340
+ * @param mint - the {@link captureMint} stamp taken at the mint's
341
+ * synchronous entry.
342
+ */
343
+ rememberProfile(argv: readonly string[], mint: MintStamp): void;
344
+ /**
345
+ * Whether a confined argv may launch now: a profile minted inside this live
346
+ * admission, or — for a `confine`→`spawn` composition with no wrapped entry
347
+ * to admit it — a profile this host minted while its authorization
348
+ * publication is still current. Cached argv, argv minted under a superseded
349
+ * publication, and argv minted elsewhere stay refused.
350
+ */
292
351
  permitsProfile(argv: readonly string[]): boolean;
352
+ /**
353
+ * The strict form: only profiles minted inside this live admission. The OPS
354
+ * execution domain owns its profile exclusively and never honors the
355
+ * unadmitted mint ledger.
356
+ */
357
+ permitsOperationProfile(argv: readonly string[]): boolean;
358
+ private permitsMintedProfile;
293
359
  get admissionRequired(): boolean;
294
360
  admissionPathsValid(): boolean;
295
361
  hasAdmission(): boolean;
@@ -1,5 +1,5 @@
1
1
  import { t as wrapMethod } from "./method-wrapper-D4oUCsEF.js";
2
- import { c as withRegistryRead } from "./registry-operation-C2ZV3Jhw.js";
2
+ import { l as withRegistryRead } from "./registry-operation-CIlmt6_c.js";
3
3
  import { l as isCanonicallyUnder } from "./roots-CmaWxRVy.js";
4
4
  import { t as routeWorkspacePath } from "./workspace-routing-D_qwMUh-.js";
5
5
  import { posix, resolve, win32 } from "node: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.15",
4
+ "version": "0.1.16",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",