@dsh-electron/dsh-plugin-multi-root-workspace 0.1.4 → 0.1.6

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 (49) hide show
  1. package/CHANGELOG.en.md +19 -2
  2. package/CHANGELOG.md +19 -2
  3. package/README.en.md +17 -13
  4. package/README.md +16 -14
  5. package/cordis.patch.yml +4 -0
  6. package/lib/client.js +320 -18
  7. package/lib/client.js.map +1 -1
  8. package/lib/{command-DyOWujPo.js → command-CTdNvwt1.js} +107 -6
  9. package/lib/command.js +1 -1
  10. package/lib/{compat-PNEKXGa6.js → compat-B6ZqbQBy.js} +23 -3
  11. package/lib/compat.js +1 -1
  12. package/lib/index.js +5 -5
  13. package/lib/{instructions-Cn1Jj5Xi.js → instructions-DfzN-wVf.js} +106 -26
  14. package/lib/instructions.js +1 -1
  15. package/lib/lsp.js +38 -0
  16. package/lib/method-wrapper-Zc-JlDP3.js +51 -0
  17. package/lib/sandbox-BRXl_FpF.js +132 -0
  18. package/lib/sandbox.js +1 -1
  19. package/lib/scope.js +12 -6
  20. package/lib/session-directory-BLOstut1.js +9 -0
  21. package/lib/tool-context-CpgIFcCB.js +6 -0
  22. package/lib/types/client/RootFileTree.d.ts +12 -0
  23. package/lib/types/client/WorkspaceFoldersAction.d.ts +3 -0
  24. package/lib/types/client/index.d.ts +13 -2
  25. package/lib/types/client/locales.d.ts +14 -0
  26. package/lib/types/client/styles.d.ts +1 -1
  27. package/lib/types/compat/client-icons.d.ts +1 -1
  28. package/lib/types/compat/client-session.d.ts +2 -0
  29. package/lib/types/compat/dsh-version.d.ts +5 -3
  30. package/lib/types/compat/instruction-overlap.d.ts +7 -0
  31. package/lib/types/compat/session-directory.d.ts +4 -0
  32. package/lib/types/compat/tool-context.d.ts +9 -0
  33. package/lib/types/compat/tool-result.d.ts +1 -1
  34. package/lib/types/compat/workspace-files.d.ts +5 -0
  35. package/lib/types/contract.d.ts +24 -5
  36. package/lib/types/index.d.ts +2 -2
  37. package/lib/types/lsp.d.ts +4 -0
  38. package/lib/types/method-wrapper.d.ts +2 -0
  39. package/lib/types/panel-files.d.ts +3 -0
  40. package/lib/types/sandbox.d.ts +2 -11
  41. package/lib/types/scope.d.ts +3 -0
  42. package/lib/types/windows-profile.d.ts +13 -0
  43. package/lib/types/windows-runner.d.ts +1 -0
  44. package/lib/types/workspace-files.d.ts +4 -0
  45. package/lib/types/workspace-routing.d.ts +11 -0
  46. package/lib/{sandbox-D8Fw95FM.js → windows-profile-CiFsFYIV.js} +64 -138
  47. package/lib/windows-runner.js +68 -0
  48. package/lib/workspace-files.js +68 -0
  49. package/package.json +90 -47
@@ -0,0 +1,132 @@
1
+ import { a as splitConfined, i as DialectUnrecognizedError, n as widenWindowsProfile, o as widenProfileArgs } from "./windows-profile-CiFsFYIV.js";
2
+ import { SandboxUnavailableError } from "@deepseek-ai/dsh-sandbox";
3
+ import { LocalSandboxProvider } from "@deepseek-ai/dsh-sandbox-local";
4
+ //#region src/compat/sandbox-confine.ts
5
+ /** Whether a value is a thenable, i.e. whether the installed `confine` is async. */
6
+ function isThenable(value) {
7
+ return typeof value?.then === "function";
8
+ }
9
+ /**
10
+ * Call the upstream `confine` and widen its result, preserving sync/async shape.
11
+ *
12
+ * @param base - the upstream `confine`, already bound to its provider.
13
+ * @param widen - the pure transformation to apply to the upstream result. It
14
+ * runs exactly once per call, on whichever release; it may throw, and the
15
+ * throw surfaces synchronously or as a rejection to match the base shape.
16
+ * @param argv - the caller's argv, forwarded verbatim.
17
+ * @param policy - the per-call policy, forwarded verbatim.
18
+ * @param signal - forwarded when the installed release accepts it.
19
+ * @returns the widened result, in the shape the installed release uses.
20
+ */
21
+ function widenConfined(base, widen, argv, policy, signal) {
22
+ const confined = base(argv, policy, signal);
23
+ return isThenable(confined) ? confined.then(widen) : widen(confined);
24
+ }
25
+ //#endregion
26
+ //#region src/sandbox.ts
27
+ /**
28
+ * `MultiRootSandboxProvider`: the kernel-sandbox dialect provider mounted as
29
+ * `ctx.sandbox`.
30
+ *
31
+ * Every confined execution in this composition funnels through `confine()` —
32
+ * the bash executor wraps `['bash','-c',command]`, and the terminal backend
33
+ * wraps its PTY spawn argv — so widening the grant here is what gives bash, PTY,
34
+ * and the fs fence one and the same root set (requirement §13). The bash
35
+ * executor itself is therefore left upstream: it never computes roots.
36
+ *
37
+ * POSIX widening lives in `./dialects.ts`, Windows capabilities in
38
+ * `./windows-profile.ts` and `./windows-runner.ts`: the upstream profile builders
39
+ * are unreachable in the published package, so the additional roots are grafted
40
+ * onto the profile `super.confine` actually produced (recognize the dialect
41
+ * structurally, clone its grant spelling, fail loudly when the shape is not
42
+ * known). This class owns only the policy that decides WHEN to graft:
43
+ *
44
+ * 1. with no additional roots, or under any mode other than `workspace-write`,
45
+ * the upstream result is returned ELEMENT FOR ELEMENT — same argv, same
46
+ * `enforcement`, same `denialSignatures`, same `runnerFailureRules`;
47
+ * 2. `enforcement`, `denialSignatures`, and `runnerFailureRules` are NEVER
48
+ * recomputed here, even when the argv is widened — the bash executor derives
49
+ * its denial and enforcement reporting from them;
50
+ * 3. only `argv` may differ, and a dialect whose shape cannot be extended fails
51
+ * loudly (`SandboxUnavailableError`) instead of silently confining to the
52
+ * primary root alone.
53
+ *
54
+ * @module @dsh-electron/dsh-plugin-multi-root-workspace/sandbox
55
+ */
56
+ /**
57
+ * Local process-sandbox provider over the multi-root scope. Registers as
58
+ * `ctx.sandbox`; the runner chain, probing, denial dialects, and Windows ACL
59
+ * grant mechanics stay the upstream implementation's.
60
+ */
61
+ var MultiRootSandboxProvider = class extends LocalSandboxProvider {
62
+ static inject = [
63
+ "multiRootCompat",
64
+ "sandboxPolicy",
65
+ "multiRootScope"
66
+ ];
67
+ /**
68
+ * Wrap `argv` so it executes confined under `policy` on this host, granting
69
+ * the scope's additional roots inside the SAME dialect profile.
70
+ *
71
+ * Under `read-only` nothing is added (the mode denies every write, additional
72
+ * roots included), and `danger-full-access` never reaches here at all: the
73
+ * bash executor and the PTY backend both hand their argv straight to the local
74
+ * runtime in that mode. Widening therefore only ever ADDS the scope's
75
+ * additional roots to an upstream `workspace-write` profile.
76
+ *
77
+ * The return shape is the INSTALLED upstream provider's: synchronous on a
78
+ * release with a synchronous `confine`, a promise on a release with an
79
+ * asynchronous one. `widenConfined` preserves whichever it is — see
80
+ * `./compat/sandbox-confine.ts`.
81
+ * @param argv - the exact argv the caller is about to spawn.
82
+ * @param policy - the file-effect policy this execution runs under.
83
+ * @param signal - cancellation, forwarded to releases that accept it.
84
+ * @returns the argv to spawn instead, plus the selected backend's facts.
85
+ */
86
+ confine(argv, policy, signal) {
87
+ const base = super.confine;
88
+ return widenConfined(base.bind(this), (confined) => this.widenForScope(confined, argv, policy), argv, policy, signal);
89
+ }
90
+ /**
91
+ * Graft the scope's additional roots onto one upstream `confine` result.
92
+ *
93
+ * Pure with respect to the upstream call: it only ever reads the scope and
94
+ * rewrites `argv`. `enforcement`, `denialSignatures` and `runnerFailureRules`
95
+ * are passed through untouched, because the bash executor derives its denial
96
+ * and enforcement reporting from them.
97
+ * @param confined - the upstream result for this call.
98
+ * @param argv - the caller's argv, needed to re-split the wrapped form.
99
+ * @param policy - the per-call policy.
100
+ * @returns the upstream result element for element, or one with a widened argv.
101
+ */
102
+ widenForScope(confined, argv, policy) {
103
+ if (policy.mode !== "workspace-write") return confined;
104
+ const scope = this.ctx.multiRootScope.resolve(policy);
105
+ if (scope.additionalRoots.length === 0) return confined;
106
+ try {
107
+ const shape = splitConfined(confined.argv, argv);
108
+ if (shape.dialect === "windows-acl") return {
109
+ ...confined,
110
+ argv: [
111
+ ...widenWindowsProfile(shape.profileArgs, scope.additionalRoots, scope.primaryRoot),
112
+ "--",
113
+ ...argv
114
+ ]
115
+ };
116
+ const profileArgs = widenProfileArgs(shape.dialect, shape.profileArgs, policy, scope.additionalRoots);
117
+ return {
118
+ ...confined,
119
+ argv: [
120
+ ...profileArgs,
121
+ "--",
122
+ ...argv
123
+ ]
124
+ };
125
+ } catch (error) {
126
+ if (!(error instanceof DialectUnrecognizedError)) throw error;
127
+ throw new SandboxUnavailableError(policy.mode, `multi-root workspace: cannot grant the additional workspace roots [${scope.additionalRoots.join(", ")}] under the primary root ${scope.primaryRoot} — ${error.message}`);
128
+ }
129
+ }
130
+ };
131
+ //#endregion
132
+ export { widenConfined as n, MultiRootSandboxProvider as t };
package/lib/sandbox.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as MultiRootSandboxProvider } from "./sandbox-D8Fw95FM.js";
1
+ import { t as MultiRootSandboxProvider } from "./sandbox-BRXl_FpF.js";
2
2
  export { MultiRootSandboxProvider, MultiRootSandboxProvider as default };
package/lib/scope.js CHANGED
@@ -71,7 +71,7 @@ const MULTI_ROOT_CONTEXT_NAME = "multi-root:scope";
71
71
  * @returns the stable topology sentence.
72
72
  */
73
73
  function renderWorkspaceRootsContext(primaryRoot, additionalRoots) {
74
- return `Current DSH workspace roots: ${JSON.stringify(additionalRoots)} are additional roots of this session's workspace. Under workspace-write they may be modified like the session workspace; the session cwd remains the primary root (${JSON.stringify(primaryRoot)}).`;
74
+ return `Current DSH workspace roots: ${JSON.stringify(additionalRoots)} are additional roots of this session's workspace. Under workspace-write they may be modified like the session workspace; the session origin remains the primary root (${JSON.stringify(primaryRoot)}); changing the working directory does not change these grants.`;
75
75
  }
76
76
  /**
77
77
  * The `ctx.multiRootScope` service. Providers ask it — never the filesystem —
@@ -81,6 +81,14 @@ function renderWorkspaceRootsContext(primaryRoot, additionalRoots) {
81
81
  var MultiRootScopeService = class extends Service {
82
82
  /** Additional roots keyed by canonical primary root; empty until roots are registered. */
83
83
  rootsByPrimary = /* @__PURE__ */ new Map();
84
+ listeners = /* @__PURE__ */ new Set();
85
+ /** Observe registry publications; consumers still resolve authority from scopeOf(). */
86
+ subscribe(listener) {
87
+ this.listeners.add(listener);
88
+ return () => {
89
+ this.listeners.delete(listener);
90
+ };
91
+ }
84
92
  constructor(ctx) {
85
93
  super(ctx, "multiRootScope");
86
94
  ctx.inject(["systemPrompt", "sandboxPolicy"], (scope) => {
@@ -132,11 +140,9 @@ var MultiRootScopeService = class extends Service {
132
140
  */
133
141
  setAdditionalRoots(primaryRoot, roots) {
134
142
  const key = canonicalPath(primaryRoot);
135
- if (roots.length === 0) {
136
- this.rootsByPrimary.delete(key);
137
- return;
138
- }
139
- this.rootsByPrimary.set(key, [...roots]);
143
+ if (roots.length === 0) this.rootsByPrimary.delete(key);
144
+ else this.rootsByPrimary.set(key, [...roots]);
145
+ for (const listener of this.listeners) listener();
140
146
  }
141
147
  };
142
148
  //#endregion
@@ -0,0 +1,9 @@
1
+ //#region src/compat/session-directory.ts
2
+ function effectiveDirectory(ctx, session) {
3
+ const service = ctx.get("workingDirectory");
4
+ if (service === void 0) return session.header.cwd;
5
+ if (typeof service.get !== "function") throw new Error("multi-root workspace: unsupported workingDirectory service");
6
+ return service.get(session);
7
+ }
8
+ //#endregion
9
+ export { effectiveDirectory as t };
@@ -0,0 +1,6 @@
1
+ //#region src/compat/tool-context.ts
2
+ function aroundTool(ctx, listener) {
3
+ ctx.on.call(ctx, "tools/execute", listener);
4
+ }
5
+ //#endregion
6
+ export { aroundTool as t };
@@ -0,0 +1,12 @@
1
+ /** Lazy additional-root tree and bounded text previews; requests carry a session and exact root. */
2
+ import { type ReactNode } from 'react';
3
+ import type { RootEntryView } from '../contract.ts';
4
+ import type { PanelClient } from './panel-client.ts';
5
+ import type { Translate } from './WorkspaceFoldersAction.tsx';
6
+ export interface RootFileTreeProps {
7
+ panel: PanelClient;
8
+ entry: RootEntryView;
9
+ sessionId: () => string | undefined;
10
+ t: Translate;
11
+ }
12
+ export declare function RootFileTree(props: RootFileTreeProps): ReactNode;
@@ -36,8 +36,11 @@ export interface WorkspaceFoldersActionProps {
36
36
  * than a value because the selection changes while the panel stays mounted.
37
37
  */
38
38
  readonly sessionId?: () => string | undefined;
39
+ readonly subscribeSession?: (listener: () => void) => () => void;
39
40
  /** Whether the sidebar renders wide content (`false` in the icon rail). */
40
41
  readonly wide?: boolean;
42
+ /** Compact launcher in the upstream Files tab's public actions slot. */
43
+ readonly compact?: boolean;
41
44
  }
42
45
  /** The sidebar footer action plus the dialog it opens. */
43
46
  export declare function WorkspaceFoldersAction(props: WorkspaceFoldersActionProps): ReactNode;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Browser half of the multi-root workspace plugin.
3
3
  *
4
- * One contribution: a sidebar footer action that opens the Workspace Folders
5
- * dialog. Everything it needs arrives through services the composition already
4
+ * Sidebar footer and optional Files actions open the Workspace Folders dialog.
5
+ * Everything it needs arrives through services the composition already
6
6
  * provides — `slots` to contribute, `locale` for its copy, `connection` for the
7
7
  * channel to the host half, and (optionally) `uiWorkspace` for the composed
8
8
  * directory picker and the client Session Controller for the current session.
@@ -16,6 +16,17 @@
16
16
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
17
17
  import { type PanelClient } from './panel-client.ts';
18
18
  import { type Translate } from './WorkspaceFoldersAction.tsx';
19
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
20
+ interface SlotMap {
21
+ 'sidebar.right.tab.files.actions': {
22
+ kind: 'list';
23
+ scope: 'session';
24
+ owner: {
25
+ readonly absolutePath: string;
26
+ };
27
+ };
28
+ }
29
+ }
19
30
  /** The footer-action cell this plugin owns. */
20
31
  export declare const SLOT_ID = "multi-root-folders";
21
32
  /** Services the client half needs before it may activate. */
@@ -12,12 +12,19 @@
12
12
  export declare const NS = "multiRootWorkspace";
13
13
  /** Chinese copy (the canonical shape). */
14
14
  export declare const zh: {
15
+ 'files.browse': string;
16
+ 'files.reload': string;
17
+ 'files.empty': string;
18
+ 'files.truncated': string;
19
+ 'files.previewTruncated': string;
15
20
  'action.label': string;
16
21
  'action.title': string;
17
22
  'panel.title': string;
18
23
  'panel.subtitle': string;
19
24
  'panel.primary': string;
20
25
  'panel.primaryNote': string;
26
+ 'panel.cwd': string;
27
+ 'panel.cwdNote': string;
21
28
  'panel.additional': string;
22
29
  'panel.empty': string;
23
30
  'panel.emptyHint': string;
@@ -66,12 +73,19 @@ export declare const zh: {
66
73
  export type Key = keyof typeof zh;
67
74
  /** English copy, pinned to the Chinese key set. */
68
75
  export declare const en: {
76
+ 'files.browse': string;
77
+ 'files.reload': string;
78
+ 'files.empty': string;
79
+ 'files.truncated': string;
80
+ 'files.previewTruncated': string;
69
81
  'action.label': string;
70
82
  'action.title': string;
71
83
  'panel.title': string;
72
84
  'panel.subtitle': string;
73
85
  'panel.primary': string;
74
86
  'panel.primaryNote': string;
87
+ 'panel.cwd': string;
88
+ 'panel.cwdNote': string;
75
89
  'panel.additional': string;
76
90
  'panel.empty': string;
77
91
  'panel.emptyHint': string;
@@ -11,4 +11,4 @@
11
11
  * @module @dsh-electron/dsh-plugin-multi-root-workspace/client/styles
12
12
  */
13
13
  /** The stylesheet text; every class is prefixed `mrfw-` to stay collision-free. */
14
- export declare const STYLES = "\n/* Sidebar footer row, mirroring the host ui-settings-general trigger: the row\n * wrapper overhangs each side by 2px (width calc(100% + 4px) against the\n * shell's 12px inline padding) so the icon's ink line lands at 18px from the\n * column edge \u2014 exactly where the Settings gear below sits. */\n.mrfw-triggerRow {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n width: calc(100% + 4px);\n margin: 4px -2px;\n}\n\n.mrfw-triggerRow.mrfw-railRow {\n width: 36px;\n margin: 8px 0 10px;\n}\n\n.mrfw-trigger {\n box-sizing: border-box;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n width: auto;\n height: 42px;\n margin: 0;\n padding: 0 10px 0 8px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-triggerIcon {\n flex: none;\n display: inline-flex;\n}\n\n.mrfw-triggerLabel {\n overflow: hidden;\n white-space: nowrap;\n}\n\n.mrfw-triggerRail {\n flex: none;\n width: 36px;\n height: 36px;\n margin: 0;\n padding: 0;\n gap: 0;\n border-radius: 50%;\n justify-content: center;\n}\n\n.mrfw-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n\n.mrfw-mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n.mrfw-dialog {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n width: min(640px, 100%);\n max-height: 100%;\n overflow: hidden;\n border: none;\n border-radius: 24px;\n background: var(--dsw-alias-bg-layer-2);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n outline: none;\n}\n\n.mrfw-header {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 22px 14px 12px 24px;\n}\n\n.mrfw-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-close {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 8px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-close:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-body {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n padding: 0 24px 24px;\n}\n\n.mrfw-description {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-alert {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.mrfw-section {\n display: flex;\n flex-direction: column;\n}\n\n.mrfw-section + .mrfw-section {\n margin-top: 8px;\n}\n\n.mrfw-sectionTitleRow {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 0 4px;\n}\n\n.mrfw-sectionTitle {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-sectionNote {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.mrfw-rowBare {\n border-bottom: none;\n}\n\n/* A root entry's two lines: the display name on top, the absolute path under\n it in the tertiary voice (the workspace rows recipe of the host sidebar). */\n.mrfw-rootText {\n display: flex;\n flex: 1 1 240px;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.mrfw-rootName {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-rootPath {\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-note {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-stateWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.mrfw-btn {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 14px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.mrfw-btnGhost:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnGhost:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.mrfw-btnDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n.mrfw-btnOutline {\n border: 0.5px solid var(--dsw-alias-border-l3);\n background: transparent;\n}\n\n.mrfw-btnOutline:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnPrimary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n/* One icon-only action: a square of the capsule family, meaning in title.\n Declared after the .mrfw-btn family so its padding wins the cascade tie:\n the capsule's 0 10px padding would otherwise squeeze the icon glyph. */\n.mrfw-iconBtn {\n width: 28px;\n padding: 0;\n border-radius: 8px;\n}\n\n.mrfw-input {\n flex: 1 1 220px;\n min-width: 0;\n height: 32px;\n padding: 0 8px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n outline: none;\n transition: border-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-input:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.mrfw-input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n.mrfw-inputInline {\n flex: 1 1 auto;\n}\n\n.mrfw-aliasEditor {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex-basis: 100%;\n}\n\n.mrfw-actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n/* One control row, one height: the capsule buttons match the 32px input. */\n.mrfw-actions .mrfw-btn {\n height: 32px;\n border-radius: 16px;\n}\n\n.mrfw-actions .mrfw-iconBtn {\n width: 32px;\n}\n";
14
+ export declare const STYLES = "\n/* Sidebar footer row, mirroring the host ui-settings-general trigger: the row\n * wrapper overhangs each side by 2px (width calc(100% + 4px) against the\n * shell's 12px inline padding) so the icon's ink line lands at 18px from the\n * column edge \u2014 exactly where the Settings gear below sits. */\n.mrfw-triggerRow {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n width: calc(100% + 4px);\n margin: 4px -2px;\n}\n\n.mrfw-triggerRow.mrfw-railRow {\n width: 36px;\n margin: 8px 0 10px;\n}\n\n.mrfw-trigger {\n box-sizing: border-box;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n width: auto;\n height: 42px;\n margin: 0;\n padding: 0 10px 0 8px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-triggerIcon {\n flex: none;\n display: inline-flex;\n}\n\n.mrfw-triggerLabel {\n overflow: hidden;\n white-space: nowrap;\n}\n\n.mrfw-triggerRail {\n flex: none;\n width: 36px;\n height: 36px;\n margin: 0;\n padding: 0;\n gap: 0;\n border-radius: 50%;\n justify-content: center;\n}\n\n.mrfw-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n\n.mrfw-mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n.mrfw-dialog {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n width: min(640px, 100%);\n max-height: 100%;\n overflow: hidden;\n border: none;\n border-radius: 24px;\n background: var(--dsw-alias-bg-layer-2);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n outline: none;\n}\n\n.mrfw-header {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 22px 14px 12px 24px;\n}\n\n.mrfw-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-close {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 8px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-close:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-body {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n padding: 0 24px 24px;\n}\n\n.mrfw-description {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-alert {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.mrfw-section {\n display: flex;\n flex-direction: column;\n}\n\n.mrfw-section + .mrfw-section {\n margin-top: 8px;\n}\n\n.mrfw-sectionTitleRow {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 0 4px;\n}\n\n.mrfw-sectionTitle {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-sectionNote {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.mrfw-rowBare {\n border-bottom: none;\n}\n\n/* A root entry's two lines: the display name on top, the absolute path under\n it in the tertiary voice (the workspace rows recipe of the host sidebar). */\n.mrfw-rootText {\n display: flex;\n flex: 1 1 240px;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.mrfw-rootName {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-rootPath {\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-note {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-stateWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.mrfw-btn {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 14px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.mrfw-btnGhost:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnGhost:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.mrfw-btnDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n.mrfw-btnOutline {\n border: 0.5px solid var(--dsw-alias-border-l3);\n background: transparent;\n}\n\n.mrfw-btnOutline:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnPrimary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n/* One icon-only action: a square of the capsule family, meaning in title.\n Declared after the .mrfw-btn family so its padding wins the cascade tie:\n the capsule's 0 10px padding would otherwise squeeze the icon glyph. */\n.mrfw-iconBtn {\n width: 28px;\n padding: 0;\n border-radius: 8px;\n}\n\n.mrfw-input {\n flex: 1 1 220px;\n min-width: 0;\n height: 32px;\n padding: 0 8px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n outline: none;\n transition: border-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-input:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.mrfw-input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n.mrfw-inputInline {\n flex: 1 1 auto;\n}\n\n.mrfw-aliasEditor {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex-basis: 100%;\n}\n\n.mrfw-actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n/* One control row, one height: the capsule buttons match the 32px input. */\n.mrfw-actions .mrfw-btn {\n height: 32px;\n border-radius: 16px;\n}\n\n.mrfw-fileTree { flex-basis: 100%; min-width: 0; }\n.mrfw-fileLevel { list-style: none; padding-left: 16px; margin: 4px 0; }\n.mrfw-fileEntry { display: block; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; text-align: left; cursor: pointer; padding: 4px; overflow-wrap: anywhere; }\n.mrfw-fileEntry:focus-visible { outline: 1px solid var(--dsw-alias-brand-primary); }\n.mrfw-filePreview { max-height: 320px; overflow: auto; padding: 8px; background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font-size: 12px; }\n.mrfw-actions .mrfw-iconBtn {\n width: 32px;\n}\n";
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * ```text
5
5
  * 0.1.5-rc.2 / 0.1.6 IconFolderClose16, IconChevronDownOutline14, … (pixel size in the name)
6
- * 0.1.7-alpha.1 through 0.1.7-rc.2 IconFolderCloseRegular, IconChevronDownOutlineRegular, … (weight in the name)
6
+ * 0.1.7-alpha.1 through 0.2.0-rc.2 IconFolderCloseRegular, IconChevronDownOutlineRegular, … (weight in the name)
7
7
  * ```
8
8
  *
9
9
  * `0.1.7-alpha.1` kept `size` as a prop and split each glyph into a Regular
@@ -30,3 +30,5 @@
30
30
  * @returns the session id, or `undefined` when no main view holds one.
31
31
  */
32
32
  export declare function currentSessionIdOf(sessions: unknown): string | undefined;
33
+ /** Observe native catalog changes when this surface provides a subscribable store. */
34
+ export declare function subscribeSessions(sessions: unknown, listener: () => void): () => void;
@@ -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"];
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"];
32
32
  /** One release this plugin claims to support. */
33
33
  export type SupportedDshRelease = typeof SUPPORTED_DSH_RELEASES[number];
34
34
  /**
@@ -42,9 +42,11 @@ 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-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-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
  /** Every package the contract inspects, required first. */
47
- 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-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"];
47
+ 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-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"];
48
+ /** Services looked up structurally; older runtimes never install these modules. */
49
+ export declare const HOST_RESOLVED_PACKAGES: readonly ["@deepseek-ai/dsh-working-directory"];
48
50
  /** How this plugin reacts to an installation it does not support. */
49
51
  export type CompatEnforcement = 'enforce' | 'warn';
50
52
  /** The environment variable that relaxes the gate. */
@@ -0,0 +1,7 @@
1
+ import type { UserMessage } from '@deepseek-ai/dsh-llm';
2
+ import type { LoadedInstructionFile } from './agent-instructions.ts';
3
+ export interface NativeInstructionCopies {
4
+ pathOf: (file: LoadedInstructionFile) => string | undefined;
5
+ withdraws: (displayPath: string) => boolean;
6
+ }
7
+ export declare function nativeInstructionCopies(messages: readonly UserMessage[], cwd: string | undefined): NativeInstructionCopies;
@@ -0,0 +1,4 @@
1
+ /** Optional upstream workingDirectory lookup; identity always stays in the Session header. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { Session } from '@deepseek-ai/dsh-session';
4
+ export declare function effectiveDirectory(ctx: Context, session: Session): string | undefined;
@@ -0,0 +1,9 @@
1
+ /** The around-dispatch seam is optional on older hosts; do not import their tool runtime. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { Agent } from '@deepseek-ai/dsh-agent';
4
+ export interface ToolCaller {
5
+ readonly agent?: Agent;
6
+ readonly callId: string;
7
+ readonly name: string;
8
+ }
9
+ export declare function aroundTool(ctx: Context, listener: (exec: ToolCaller, next: () => Promise<unknown>) => Promise<unknown>): void;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * ```text
5
5
  * 0.1.5-rc.2 / 0.1.6 content[0].isError on the tool-result block
6
- * 0.1.7-alpha.1 through 0.1.7-rc.2 message.isError on the tool-role message
6
+ * 0.1.7-alpha.1 through 0.2.0-rc.2 message.isError on the tool-role message
7
7
  * ```
8
8
  *
9
9
  * A failed `read` / `write` / `edit` must not make a directory worth examining
@@ -0,0 +1,5 @@
1
+ /** Old releases watch a session; newer ones watch a path. Probe arity, never version. */
2
+ import type { WorkspaceFiles, WorkspaceFileScope, WorkspaceFileWatchFrame } from '@deepseek-ai/dsh-api-workspace-files';
3
+ type PathWatch = (this: WorkspaceFiles, scope: WorkspaceFileScope, path: string, signal: AbortSignal) => AsyncIterable<WorkspaceFileWatchFrame>;
4
+ export declare function adaptPathWatch(service: WorkspaceFiles, wrap: (original: PathWatch) => PathWatch): () => void;
5
+ export {};
@@ -12,8 +12,9 @@
12
12
  * - **One shape per endpoint.** {@link PanelResponseMap} is the single mapping
13
13
  * from endpoint to response type, so the panel cannot silently decode one
14
14
  * endpoint's answer as another's. `reveal` answers with
15
- * {@link RevealedView}; every other endpoint answers with a whole
16
- * {@link RootsView}. The host builds those answers in `command.ts` and the
15
+ * {@link RevealedView}; root-management endpoints answer with
16
+ * {@link RootsView}, and browsing returns {@link FilesView} or {@link FilePreview}.
17
+ * The host builds those answers in `command.ts` and the
17
18
  * panel parses them here — one definition, two consumers.
18
19
  * - **Runtime validation at the boundary.** The payload arrives from a browser
19
20
  * and the answer arrives from the host; neither is trusted to match the
@@ -30,9 +31,21 @@
30
31
  /** The absolute logical channel both halves speak over. */
31
32
  export declare const PANEL_CHANNEL = "/multi-root-workspace";
32
33
  /** Endpoint names, relative to {@link PANEL_CHANNEL}. */
33
- export type PanelEndpoint = 'list' | 'add' | 'remove' | 'alias' | 'move' | 'reveal';
34
+ export type PanelEndpoint = 'list' | 'add' | 'remove' | 'alias' | 'move' | 'reveal' | 'files' | 'readFile';
34
35
  /** Every endpoint name, for exhaustive iteration in tests and callers. */
35
36
  export declare const PANEL_ENDPOINTS: readonly PanelEndpoint[];
37
+ export interface FilesView {
38
+ readonly path: string;
39
+ readonly entries: readonly {
40
+ readonly name: string;
41
+ readonly type: 'file' | 'directory' | 'other';
42
+ }[];
43
+ readonly truncated: boolean;
44
+ }
45
+ export interface FilePreview {
46
+ readonly text: string;
47
+ readonly eof: boolean;
48
+ }
36
49
  /**
37
50
  * How one registered root currently stands, as the panel renders it. Kept in
38
51
  * step with the root vocabulary in `src/roots.ts` deliberately: this module is
@@ -64,7 +77,7 @@ export interface RootEntryView {
64
77
  readonly path: string;
65
78
  readonly addedAt: string;
66
79
  }
67
- /** The panel's whole view of one workspace root; every endpoint but `reveal` answers with this. */
80
+ /** The panel's whole view, returned by root-management endpoints. */
68
81
  export interface RootsView {
69
82
  /** The canonical workspace root these registrations belong to. */
70
83
  readonly primaryRoot: string;
@@ -74,6 +87,8 @@ export interface RootsView {
74
87
  * basename.
75
88
  */
76
89
  readonly primaryName?: string;
90
+ /** Execution directory only; never a registry key or an authorization grant. */
91
+ readonly effectiveCwd?: string;
77
92
  /** The additional roots, in registry order. */
78
93
  readonly roots: readonly RootView[];
79
94
  /** Set when the store itself could not be read; the panel shows this verbatim. */
@@ -92,6 +107,8 @@ export interface PanelResponseMap {
92
107
  readonly alias: RootsView;
93
108
  readonly move: RootsView;
94
109
  readonly reveal: RevealedView;
110
+ readonly files: FilesView;
111
+ readonly readFile: FilePreview;
95
112
  }
96
113
  /**
97
114
  * The fields a request may carry. `sessionId` is required on every endpoint:
@@ -106,7 +123,7 @@ export interface PanelRequest {
106
123
  readonly entry?: RootEntryView;
107
124
  /** Compatibility reference; accepted only when the id is unique. */
108
125
  readonly id?: string;
109
- /** Candidate directory (add). */
126
+ /** Candidate directory (add), or root-relative browsing path (files/readFile). */
110
127
  readonly path?: string;
111
128
  /** Display alias (add/alias); empty clears it. */
112
129
  readonly alias?: string;
@@ -135,6 +152,8 @@ export type Parsed<T> = {
135
152
  readonly ok: false;
136
153
  readonly message: string;
137
154
  };
155
+ export declare function parseFilesView(value: unknown): Parsed<FilesView>;
156
+ export declare function parseFilePreview(value: unknown): Parsed<FilePreview>;
138
157
  /**
139
158
  * Validate the payload the host received for one endpoint.
140
159
  * @param endpoint - the endpoint the channel dispatched (the RPC method).
@@ -44,8 +44,8 @@ export { availableRoots, canonicalRoot, classifyStoredRoots, expandRootInput, in
44
44
  export type { AdditionalRootId, RegisteredRoot, RootRef, RootState, RootStatus, RootValidationCode, } from './roots.ts';
45
45
  export { COMMAND_NAME, parseFoldersCommand, renderRootsReport, resolvePanelPrimaryRoot, revealArgv } from './command.ts';
46
46
  export type { FoldersCommand } from './command.ts';
47
- export { PANEL_CHANNEL, PANEL_ENDPOINTS, parsePanelCall, parseRevealedView, parseRootsView } from './contract.ts';
48
- export type { PanelCall, PanelEndpoint, PanelFailure, PanelRequest, PanelResponseMap, RevealedView, RootView, RootsView, } from './contract.ts';
47
+ export { PANEL_CHANNEL, PANEL_ENDPOINTS, parsePanelCall, parseRevealedView, parseRootsView, parseFilesView, parseFilePreview } from './contract.ts';
48
+ export type { FilesView, FilePreview, PanelCall, PanelEndpoint, PanelFailure, PanelRequest, PanelResponseMap, RevealedView, RootView, RootsView, } from './contract.ts';
49
49
  /**
50
50
  * Carrier plugin face for the bare-package-name loader row (see the module
51
51
  * doc). It deliberately provides nothing: every host responsibility is owned
@@ -0,0 +1,4 @@
1
+ /** Add root routing at the public LSP query seam; upstream owns servers and disposal. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ export declare const inject: string[];
4
+ export declare function apply(ctx: Context): void;
@@ -0,0 +1,2 @@
1
+ /** Wrap a public method on a Cordis service without capturing a caller-context proxy. */
2
+ export declare function wrapMethod<T extends object, K extends keyof T>(target: T, key: K, wrap: (original: T[K]) => T[K]): () => void;
@@ -0,0 +1,3 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { PanelCall, FilePreview, FilesView } from './contract.ts';
3
+ export declare function browseRoot(ctx: Context, primaryRoot: string, request: PanelCall, signal: AbortSignal): Promise<FilesView | FilePreview>;
@@ -8,7 +8,8 @@
8
8
  * and the fs fence one and the same root set (requirement §13). The bash
9
9
  * executor itself is therefore left upstream: it never computes roots.
10
10
  *
11
- * The widening itself lives in `./dialects.ts`: the upstream profile builders
11
+ * POSIX widening lives in `./dialects.ts`, Windows capabilities in
12
+ * `./windows-profile.ts` and `./windows-runner.ts`: the upstream profile builders
12
13
  * are unreachable in the published package, so the additional roots are grafted
13
14
  * onto the profile `super.confine` actually produced (recognize the dialect
14
15
  * structurally, clone its grant spelling, fail loudly when the shape is not
@@ -38,8 +39,6 @@ export type { Config };
38
39
  */
39
40
  export declare class MultiRootSandboxProvider extends LocalSandboxProvider {
40
41
  static inject: string[];
41
- /** Whether the Windows ACL limitation has already been reported (once per provider). */
42
- private warnedAboutWindowsAcl;
43
42
  /**
44
43
  * Wrap `argv` so it executes confined under `policy` on this host, granting
45
44
  * the scope's additional roots inside the SAME dialect profile.
@@ -73,13 +72,5 @@ export declare class MultiRootSandboxProvider extends LocalSandboxProvider {
73
72
  * @returns the upstream result element for element, or one with a widened argv.
74
73
  */
75
74
  private widenForScope;
76
- /**
77
- * Report the documented first-release limitation exactly once: the Windows ACL
78
- * rung grants through per-workspace write SIDs rather than argv paths, so
79
- * additional roots stay unenforced for confined commands there while the
80
- * in-process filesystem fence still allows them.
81
- * @param rootCount - how many additional roots the scope carries.
82
- */
83
- private warnAboutWindowsAcl;
84
75
  }
85
76
  export default MultiRootSandboxProvider;
@@ -93,6 +93,9 @@ export declare function renderWorkspaceRootsContext(primaryRoot: string, additio
93
93
  export declare class MultiRootScopeService extends Service {
94
94
  /** Additional roots keyed by canonical primary root; empty until roots are registered. */
95
95
  private readonly rootsByPrimary;
96
+ private readonly listeners;
97
+ /** Observe registry publications; consumers still resolve authority from scopeOf(). */
98
+ subscribe(listener: () => void): () => void;
96
99
  constructor(ctx: Context);
97
100
  /**
98
101
  * Resolve the scope of one confined call.
@@ -0,0 +1,13 @@
1
+ /** A root-set capability: removing or adding a root changes the token's identity. */
2
+ export declare function workspaceScopeSid(roots: readonly string[]): string;
3
+ export interface WindowsProfile {
4
+ workspace: string;
5
+ temp: string;
6
+ mode: 'workspace-write';
7
+ additionalRoots: string[];
8
+ writeSid?: string;
9
+ tempWriteSid?: string;
10
+ }
11
+ /** Strictly recognize the upstream public runner contract, failing closed on new flags. */
12
+ export declare function parseWindowsProfile(args: readonly string[], allowAdditional?: boolean): WindowsProfile;
13
+ export declare function widenWindowsProfile(profile: readonly string[], roots: readonly string[], primaryRoot?: string): string[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ /** Extend the existing workspaceFiles namespace without replacing its codecs or read caps. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ export declare const inject: string[];
4
+ export declare function apply(ctx: Context): void;
@@ -0,0 +1,11 @@
1
+ /** Shared, canonical root selection for read-only workspace consumers. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { FsTarget } from '@deepseek-ai/dsh-fs';
4
+ export interface WorkspaceRoute {
5
+ primaryRoot: string;
6
+ workspaceRoot: string;
7
+ target: FsTarget;
8
+ absolutePath: string;
9
+ }
10
+ /** Resolve relative paths against the primary cwd; never search other roots by basename. */
11
+ export declare function routeWorkspacePath(ctx: Context, primaryRoot: string, path: string, signal?: AbortSignal, cwd?: string): Promise<WorkspaceRoute | undefined>;