@yadsh/dsh-session-scope 0.5.0

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 (53) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +110 -0
  3. package/compatibility.json +20 -0
  4. package/cordis.patch.yml +7 -0
  5. package/lib/client.d.ts +1 -0
  6. package/lib/client.d.ts.map +1 -0
  7. package/lib/client.js +716 -0
  8. package/lib/client.js.map +1 -0
  9. package/lib/core.d.ts +204 -0
  10. package/lib/core.d.ts.map +1 -0
  11. package/lib/core.js +375 -0
  12. package/lib/core.js.map +1 -0
  13. package/lib/host-api.d.ts +23 -0
  14. package/lib/host-api.d.ts.map +1 -0
  15. package/lib/host-api.js +40 -0
  16. package/lib/host-api.js.map +1 -0
  17. package/lib/index.d.ts +32 -0
  18. package/lib/index.d.ts.map +1 -0
  19. package/lib/index.js +594 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/scope-context.d.ts +3 -0
  22. package/lib/scope-context.d.ts.map +1 -0
  23. package/lib/scope-context.js +29 -0
  24. package/lib/scope-context.js.map +1 -0
  25. package/lib/scope-delegation.d.ts +23 -0
  26. package/lib/scope-delegation.d.ts.map +1 -0
  27. package/lib/scope-delegation.js +49 -0
  28. package/lib/scope-delegation.js.map +1 -0
  29. package/lib/scope-fs.d.ts +47 -0
  30. package/lib/scope-fs.d.ts.map +1 -0
  31. package/lib/scope-fs.js +150 -0
  32. package/lib/scope-fs.js.map +1 -0
  33. package/lib/scope-processes.d.ts +31 -0
  34. package/lib/scope-processes.d.ts.map +1 -0
  35. package/lib/scope-processes.js +71 -0
  36. package/lib/scope-processes.js.map +1 -0
  37. package/lib/scope-sandbox-linux.d.ts +35 -0
  38. package/lib/scope-sandbox-linux.d.ts.map +1 -0
  39. package/lib/scope-sandbox-linux.js +177 -0
  40. package/lib/scope-sandbox-linux.js.map +1 -0
  41. package/lib/scope-visibility.d.ts +14 -0
  42. package/lib/scope-visibility.d.ts.map +1 -0
  43. package/lib/scope-visibility.js +62 -0
  44. package/lib/scope-visibility.js.map +1 -0
  45. package/lib/session-scope.d.ts +54 -0
  46. package/lib/session-scope.d.ts.map +1 -0
  47. package/lib/session-scope.js +200 -0
  48. package/lib/session-scope.js.map +1 -0
  49. package/lib/tool-guard.d.ts +52 -0
  50. package/lib/tool-guard.d.ts.map +1 -0
  51. package/lib/tool-guard.js +233 -0
  52. package/lib/tool-guard.js.map +1 -0
  53. package/package.json +150 -0
@@ -0,0 +1,177 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { posix } from "node:path";
3
+ import { SESSION_SCOPE_ERROR, SessionScopeError, } from "./session-scope.js";
4
+ /** Same-world, non-serialized carrier added to DSH's per-call sandbox policy. */
5
+ export const SESSION_SCOPE_POLICY = Symbol.for("dsh-session-scope/policy");
6
+ const BWRAP_READ_ONLY_PROFILE = [
7
+ "bwrap",
8
+ "--ro-bind", "/", "/",
9
+ "--dev", "/dev",
10
+ "--proc", "/proc",
11
+ "--die-with-parent",
12
+ ];
13
+ const BWRAP_STAGING_ROOT = "/dev/.dsh-session-scope";
14
+ function unavailable(detail) {
15
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.ISOLATION_UNAVAILABLE, `Isolated session scope is unavailable: ${detail}.`);
16
+ }
17
+ function normalizedAbsolute(path, label) {
18
+ if (!posix.isAbsolute(path) || path.includes("\0"))
19
+ unavailable(`invalid ${label}`);
20
+ return posix.normalize(path);
21
+ }
22
+ function sameArray(left, right) {
23
+ return left.length === right.length && left.every((value, index) => value === right[index]);
24
+ }
25
+ function expectedProfile(policy) {
26
+ if (policy.mode === "read-only")
27
+ return [...BWRAP_READ_ONLY_PROFILE];
28
+ if (policy.mode === "workspace-write") {
29
+ return [
30
+ ...BWRAP_READ_ONLY_PROFILE,
31
+ "--tmpfs", "/tmp",
32
+ "--bind", policy.workspaceRoot, policy.workspaceRoot,
33
+ ];
34
+ }
35
+ return unavailable("the active permission mode cannot be confined by the DSH bwrap provider");
36
+ }
37
+ function separatorIndex(argv) {
38
+ const index = argv.indexOf("--");
39
+ if (index < 0 || index === argv.length - 1)
40
+ unavailable("the sandbox provider returned an invalid bwrap invocation");
41
+ return index;
42
+ }
43
+ /** Require the exact, versioned DSH bwrap profile this plugin knows how to narrow. */
44
+ export function isSupportedBwrapInvocation(confined, policy) {
45
+ if (confined.enforcement !== "full")
46
+ return false;
47
+ try {
48
+ const separator = separatorIndex(confined.argv);
49
+ return sameArray(confined.argv.slice(0, separator), expectedProfile(policy));
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
55
+ /**
56
+ * Probe the official provider seam. The local provider performs its own
57
+ * functional bwrap probe before returning this direct profile.
58
+ */
59
+ export function detectBwrapIsolation(provider, workspaceRoot, platform = process.platform, execute = (argv) => {
60
+ const result = spawnSync(argv[0], argv.slice(1), { stdio: "ignore", timeout: 5_000 });
61
+ return result.error === undefined && result.status === 0;
62
+ }) {
63
+ if (platform !== "linux" || provider === undefined)
64
+ return false;
65
+ try {
66
+ const policy = { mode: "read-only", workspaceRoot };
67
+ const confined = provider.confine(["true"], policy);
68
+ if (!isSupportedBwrapInvocation(confined, policy))
69
+ return false;
70
+ const probe = confineIsolatedBwrap(confined, policy, {
71
+ mode: "isolated",
72
+ workspaceRoot,
73
+ roots: [],
74
+ navigationRoots: [workspaceRoot],
75
+ }, workspaceRoot);
76
+ return execute(probe.argv);
77
+ }
78
+ catch {
79
+ return false;
80
+ }
81
+ }
82
+ function isWithin(path, root) {
83
+ return path === root || path.startsWith(root === "/" ? "/" : `${root}/`);
84
+ }
85
+ function visibleWorkingDirectory(requested, workspaceRoot, roots) {
86
+ const workdir = normalizedAbsolute(requested === undefined ? workspaceRoot : posix.resolve(workspaceRoot, requested), "process working directory");
87
+ if (!isWithin(workdir, workspaceRoot))
88
+ unavailable("the process working directory is outside the session workspace");
89
+ const content = roots.some((root) => isWithin(workdir, root));
90
+ const navigation = workdir === workspaceRoot || roots.some((root) => isWithin(root, workdir));
91
+ if (!content && !navigation)
92
+ unavailable("the process working directory is outside the active session scope");
93
+ return workdir;
94
+ }
95
+ function mountPointDirectories(workspaceRoot, roots) {
96
+ const result = [];
97
+ const seen = new Set();
98
+ for (const root of roots) {
99
+ if (root === workspaceRoot)
100
+ continue;
101
+ const relative = posix.relative(workspaceRoot, root);
102
+ let current = workspaceRoot;
103
+ for (const segment of relative.split("/").filter(Boolean)) {
104
+ current = posix.join(current, segment);
105
+ if (!seen.has(current)) {
106
+ seen.add(current);
107
+ result.push(current);
108
+ }
109
+ }
110
+ }
111
+ return result;
112
+ }
113
+ /**
114
+ * Replace DSH's whole-workspace bwrap view with an empty workspace plus only
115
+ * the selected roots. Unknown argv dialects and partial enforcement fail closed.
116
+ */
117
+ export function confineIsolatedBwrap(confined, policy, scope, requestedWorkingDirectory) {
118
+ if (scope.mode !== "isolated")
119
+ return confined;
120
+ if (confined.enforcement !== "full")
121
+ unavailable("the selected sandbox backend reports partial enforcement");
122
+ const workspaceRoot = normalizedAbsolute(scope.workspaceRoot, "session workspace");
123
+ if (workspaceRoot === "/")
124
+ unavailable("the filesystem root cannot be used as an isolated workspace");
125
+ if (isWithin(workspaceRoot, "/dev"))
126
+ unavailable("a workspace under /dev cannot be isolated safely");
127
+ if (normalizedAbsolute(policy.workspaceRoot, "sandbox workspace") !== workspaceRoot) {
128
+ unavailable("the sandbox and session workspace boundaries do not match");
129
+ }
130
+ if (!isSupportedBwrapInvocation(confined, policy)) {
131
+ unavailable("the sandbox provider is not the supported DSH bwrap profile");
132
+ }
133
+ const roots = scope.roots.map((root) => normalizedAbsolute(root, "scope root"));
134
+ if (roots.some((root) => !isWithin(root, workspaceRoot)))
135
+ unavailable("a selected root is outside the session workspace");
136
+ const workdir = visibleWorkingDirectory(requestedWorkingDirectory, workspaceRoot, roots);
137
+ const separator = separatorIndex(confined.argv);
138
+ const command = confined.argv.slice(separator + 1);
139
+ // Selecting the workspace root means the requested visible view is already
140
+ // the whole workspace. Still pin cwd after bwrap establishes its mounts.
141
+ if (roots.includes(workspaceRoot)) {
142
+ return {
143
+ ...confined,
144
+ argv: [...confined.argv.slice(0, separator), "--chdir", workdir, "--", ...command],
145
+ };
146
+ }
147
+ const profile = confined.argv.slice(0, separator);
148
+ if (policy.mode === "workspace-write")
149
+ profile.splice(profile.length - 3, 3);
150
+ const bindFlag = policy.mode === "workspace-write" ? "--bind" : "--ro-bind";
151
+ const mounts = ["--dir", BWRAP_STAGING_ROOT, "--tmpfs", BWRAP_STAGING_ROOT];
152
+ for (let index = 0; index < roots.length; index += 1) {
153
+ const staging = `${BWRAP_STAGING_ROOT}/${index}`;
154
+ mounts.push("--dir", staging, bindFlag, roots[index], staging);
155
+ }
156
+ mounts.push("--tmpfs", workspaceRoot);
157
+ for (const directory of mountPointDirectories(workspaceRoot, roots))
158
+ mounts.push("--dir", directory);
159
+ for (let index = 0; index < roots.length; index += 1) {
160
+ mounts.push(bindFlag, `${BWRAP_STAGING_ROOT}/${index}`, roots[index]);
161
+ }
162
+ // Drop the alternate path to selected content and leave no new writable
163
+ // scratch area behind, including under read-only permission.
164
+ mounts.push("--tmpfs", BWRAP_STAGING_ROOT, "--remount-ro", BWRAP_STAGING_ROOT);
165
+ return {
166
+ ...confined,
167
+ argv: [...profile, ...mounts, "--chdir", workdir, "--", ...command],
168
+ };
169
+ }
170
+ /** Attach the effective scope without changing DSH's public policy shape. */
171
+ export function attachSessionScopePolicy(policy, scope) {
172
+ return Object.assign({}, policy, { [SESSION_SCOPE_POLICY]: scope });
173
+ }
174
+ export function sessionScopeFromPolicy(policy) {
175
+ return policy[SESSION_SCOPE_POLICY];
176
+ }
177
+ //# sourceMappingURL=scope-sandbox-linux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-sandbox-linux.js","sourceRoot":"","sources":["../src/scope-sandbox-linux.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { DirectoryListing } from "./core.js";
2
+ import { type EffectiveSessionScope } from "./session-scope.js";
3
+ export type ScopePathVisibility = "content" | "navigation" | "denied";
4
+ export type ScopedFilesystemOperation = "read" | "write" | "list" | "search";
5
+ export declare function classifyScopePath(scope: EffectiveSessionScope, target: string, caseSensitive?: boolean): ScopePathVisibility;
6
+ export declare function assertScopeAccess(scope: EffectiveSessionScope, target: string, operation: ScopedFilesystemOperation, caseSensitive?: boolean): ScopePathVisibility;
7
+ /** Filter a one-level listing without disclosing hidden sibling names. */
8
+ export declare function filterScopeDirectoryListing(scope: EffectiveSessionScope, listing: DirectoryListing, caseSensitive?: boolean): DirectoryListing;
9
+ /**
10
+ * Reduce a glob/grep/search request to content roots. Callers execute one
11
+ * search per returned root and merge results; an empty array is a denial.
12
+ */
13
+ export declare function scopedSearchRoots(scope: EffectiveSessionScope, requestedRoot: string, caseSensitive?: boolean): string[];
14
+ //# sourceMappingURL=scope-visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-visibility.d.ts","sourceRoot":"","sources":["../src/scope-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElE,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;AACtE,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAM7E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,MAAM,EACd,aAAa,UAA+B,GAC3C,mBAAmB,CAQrB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,yBAAyB,EACpC,aAAa,UAA+B,GAC3C,mBAAmB,CAUrB;AAaD,0EAA0E;AAC1E,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,gBAAgB,EACzB,aAAa,UAA+B,GAC3C,gBAAgB,CAUlB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,qBAAqB,EAC5B,aAAa,EAAE,MAAM,EACrB,aAAa,UAA+B,GAC3C,MAAM,EAAE,CAgBV"}
@@ -0,0 +1,62 @@
1
+ import { isLexicallyUnder } from "./core.js";
2
+ import { SESSION_SCOPE_ERROR, SessionScopeError, } from "./session-scope.js";
3
+ function samePath(left, right, caseSensitive) {
4
+ return caseSensitive ? left === right : left.toLowerCase() === right.toLowerCase();
5
+ }
6
+ export function classifyScopePath(scope, target, caseSensitive = process.platform !== "win32") {
7
+ if (scope.mode === "full")
8
+ return "content";
9
+ // Scope narrows only the session workspace. OS/user paths outside it remain
10
+ // governed by the ordinary DSH permission policy (see the spec non-goals).
11
+ if (!isLexicallyUnder(target, scope.workspaceRoot, caseSensitive))
12
+ return "content";
13
+ if (scope.roots.some((root) => isLexicallyUnder(target, root, caseSensitive)))
14
+ return "content";
15
+ if (scope.navigationRoots.some((root) => samePath(target, root, caseSensitive)))
16
+ return "navigation";
17
+ return "denied";
18
+ }
19
+ export function assertScopeAccess(scope, target, operation, caseSensitive = process.platform !== "win32") {
20
+ const visibility = classifyScopePath(scope, target, caseSensitive);
21
+ const allowed = visibility === "content" || operation === "list" && visibility === "navigation";
22
+ if (!allowed) {
23
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.DENIED, "Path is outside the active session scope.");
24
+ }
25
+ return visibility;
26
+ }
27
+ function entryLeadsToContent(entry, scope, caseSensitive) {
28
+ return scope.roots.some((root) => isLexicallyUnder(root, entry.path, caseSensitive)
29
+ || isLexicallyUnder(entry.path, root, caseSensitive));
30
+ }
31
+ /** Filter a one-level listing without disclosing hidden sibling names. */
32
+ export function filterScopeDirectoryListing(scope, listing, caseSensitive = process.platform !== "win32") {
33
+ const visibility = assertScopeAccess(scope, listing.path, "list", caseSensitive);
34
+ if (scope.mode === "full" || visibility === "content")
35
+ return listing;
36
+ return {
37
+ ...listing,
38
+ crumbs: listing.crumbs.filter((crumb) => classifyScopePath(scope, crumb.path, caseSensitive) !== "denied"),
39
+ entries: listing.entries.filter((entry) => entryLeadsToContent(entry, scope, caseSensitive)),
40
+ };
41
+ }
42
+ /**
43
+ * Reduce a glob/grep/search request to content roots. Callers execute one
44
+ * search per returned root and merge results; an empty array is a denial.
45
+ */
46
+ export function scopedSearchRoots(scope, requestedRoot, caseSensitive = process.platform !== "win32") {
47
+ if (scope.mode === "full")
48
+ return [requestedRoot];
49
+ const insideWorkspace = isLexicallyUnder(requestedRoot, scope.workspaceRoot, caseSensitive);
50
+ const containsWorkspace = isLexicallyUnder(scope.workspaceRoot, requestedRoot, caseSensitive);
51
+ if (!insideWorkspace && !containsWorkspace)
52
+ return [requestedRoot];
53
+ if (scope.roots.some((root) => isLexicallyUnder(requestedRoot, root, caseSensitive))) {
54
+ return [requestedRoot];
55
+ }
56
+ const roots = scope.roots.filter((root) => isLexicallyUnder(root, requestedRoot, caseSensitive));
57
+ if (roots.length === 0) {
58
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.DENIED, "Path is outside the active session scope.");
59
+ }
60
+ return roots;
61
+ }
62
+ //# sourceMappingURL=scope-visibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-visibility.js","sourceRoot":"","sources":["../src/scope-visibility.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,GAElB,MAAM,oBAAoB,CAAC;AAK5B,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAa,EAAE,aAAsB;IACnE,OAAO,aAAa,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC;AACrF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAA4B,EAC5B,MAAc,EACd,aAAa,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO;IAE5C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,SAAS,CAAC;IAC5C,4EAA4E;IAC5E,2EAA2E;IAC3E,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,EAAE,aAAa,CAAC;QAAE,OAAO,SAAS,CAAC;IACpF,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAChG,IAAI,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QAAE,OAAO,YAAY,CAAC;IACrG,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAA4B,EAC5B,MAAc,EACd,SAAoC,EACpC,aAAa,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO;IAE5C,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,UAAU,KAAK,SAAS,IAAI,SAAS,KAAK,MAAM,IAAI,UAAU,KAAK,YAAY,CAAC;IAChG,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,iBAAiB,CACzB,mBAAmB,CAAC,MAAM,EAC1B,2CAA2C,CAC5C,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,mBAAmB,CAC1B,KAAqB,EACrB,KAA4B,EAC5B,aAAsB;IAEtB,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/B,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC;WAC9C,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,CACrD,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,2BAA2B,CACzC,KAA4B,EAC5B,OAAyB,EACzB,aAAa,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO;IAE5C,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACjF,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IACtE,OAAO;QACL,GAAG,OAAO;QACV,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CACtC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,KAAK,QAAQ,CACjE;QACD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;KAC7F,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAA4B,EAC5B,aAAqB,EACrB,aAAa,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO;IAE5C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAClD,MAAM,eAAe,GAAG,gBAAgB,CAAC,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAC5F,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,KAAK,CAAC,aAAa,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;IAC9F,IAAI,CAAC,eAAe,IAAI,CAAC,iBAAiB;QAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC;QACrF,OAAO,CAAC,aAAa,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC;IACjG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,iBAAiB,CACzB,mBAAmB,CAAC,MAAM,EAC1B,2CAA2C,CAC5C,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { type SessionEvent } from "./core.js";
2
+ export declare const SESSION_SCOPE_EVENT: "session-scope/set";
3
+ export declare const SESSION_SCOPE_ERROR: Readonly<{
4
+ readonly DENIED: "SESSION_SCOPE_DENIED";
5
+ readonly INVALID_ROOT: "SESSION_SCOPE_INVALID_ROOT";
6
+ readonly OUTSIDE_WORKSPACE: "SESSION_SCOPE_OUTSIDE_WORKSPACE";
7
+ readonly SYMLINK_ESCAPE: "SESSION_SCOPE_SYMLINK_ESCAPE";
8
+ readonly ISOLATION_UNAVAILABLE: "SESSION_SCOPE_ISOLATION_UNAVAILABLE";
9
+ readonly PROCESS_ACTIVE: "SESSION_SCOPE_PROCESS_ACTIVE";
10
+ readonly PARENT_UNAVAILABLE: "SESSION_SCOPE_PARENT_UNAVAILABLE";
11
+ readonly STALE_WORKSPACE: "SESSION_SCOPE_STALE_WORKSPACE";
12
+ }>;
13
+ export type SessionScopeErrorCode = (typeof SESSION_SCOPE_ERROR)[keyof typeof SESSION_SCOPE_ERROR];
14
+ export type SessionScopeMode = "full" | "focused" | "isolated";
15
+ export type SessionScopeSource = "ui" | "command" | "migration" | "delegation";
16
+ export interface SessionScopeEventData {
17
+ version: 1;
18
+ mode: SessionScopeMode;
19
+ roots: string[];
20
+ workspaceRoot: string;
21
+ source?: SessionScopeSource;
22
+ }
23
+ export interface SessionHeader {
24
+ cwd?: string;
25
+ }
26
+ export interface EffectiveSessionScope {
27
+ mode: SessionScopeMode;
28
+ workspaceRoot: string;
29
+ roots: string[];
30
+ navigationRoots: string[];
31
+ }
32
+ export interface ScopePathServices {
33
+ canonical(path: string): string;
34
+ isDirectory(path: string): boolean;
35
+ }
36
+ export interface NormalizeSessionScopeOptions {
37
+ allowExternalRoots?: boolean;
38
+ caseSensitive?: boolean;
39
+ paths?: ScopePathServices;
40
+ }
41
+ export declare class SessionScopeError extends Error {
42
+ readonly code: SessionScopeErrorCode;
43
+ constructor(code: SessionScopeErrorCode, message: string);
44
+ }
45
+ /** Drop duplicate and descendant roots already covered by a selected parent. */
46
+ export declare function collapseNestedRoots(roots: readonly string[], caseSensitive?: boolean): string[];
47
+ /** Ancestors visible only for navigation from the workspace to content roots. */
48
+ export declare function navigationRootsFor(workspaceRoot: string, roots: readonly string[], caseSensitive?: boolean): string[];
49
+ /** Validate and canonicalize roots immediately before persisting a snapshot. */
50
+ export declare function normalizeSessionScopeRoots(input: unknown, workspaceRoot: string, options?: NormalizeSessionScopeOptions): string[];
51
+ /** Fold new durable state, falling back to the upstream event only for legacy sessions. */
52
+ export declare function effectiveSessionScope(events: readonly SessionEvent[], sessionHeader?: SessionHeader, caseSensitive?: boolean): EffectiveSessionScope;
53
+ export declare function createSessionScopeEvent(mode: SessionScopeMode, roots: unknown, workspaceRoot: string, source: SessionScopeSource, options?: NormalizeSessionScopeOptions): SessionScopeEventData;
54
+ //# sourceMappingURL=session-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scope.d.ts","sourceRoot":"","sources":["../src/session-scope.ts"],"names":[],"mappings":"AAGA,OAAO,EAML,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAEnB,eAAO,MAAM,mBAAmB,EAAG,mBAA4B,CAAC;AAEhE,eAAO,MAAM,mBAAmB;qBACtB,sBAAsB;2BAChB,4BAA4B;gCACvB,iCAAiC;6BACpC,8BAA8B;oCACvB,qCAAqC;6BAC5C,8BAA8B;iCAC1B,kCAAkC;8BACrC,+BAA+B;EACvC,CAAC;AAEZ,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC;AACnG,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAC/D,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAC;AAE/E,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,gBAAgB,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,4BAA4B;IAC3C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAgBD,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAErC,YAAY,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,EAIvD;CACF;AAMD,gFAAgF;AAChF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,aAAa,UAA+B,GAC3C,MAAM,EAAE,CAkBV;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,aAAa,UAA+B,GAC3C,MAAM,EAAE,CA6BV;AAED,gFAAgF;AAChF,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,4BAAiC,GACzC,MAAM,EAAE,CAgDV;AA6BD,2FAA2F;AAC3F,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,aAAa,GAAE,aAAkB,EACjC,aAAa,UAA+B,GAC3C,qBAAqB,CA4CvB;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,kBAAkB,EAC1B,OAAO,CAAC,EAAE,4BAA4B,GACrC,qBAAqB,CASvB"}
@@ -0,0 +1,200 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { dirname, isAbsolute, resolve } from "node:path";
3
+ import { MAX_ROOTS, SELECTION_EVENT, canonicalPath, isLexicallyUnder, selectionOf, } from "./core.js";
4
+ export const SESSION_SCOPE_EVENT = "session-scope/set";
5
+ export const SESSION_SCOPE_ERROR = Object.freeze({
6
+ DENIED: "SESSION_SCOPE_DENIED",
7
+ INVALID_ROOT: "SESSION_SCOPE_INVALID_ROOT",
8
+ OUTSIDE_WORKSPACE: "SESSION_SCOPE_OUTSIDE_WORKSPACE",
9
+ SYMLINK_ESCAPE: "SESSION_SCOPE_SYMLINK_ESCAPE",
10
+ ISOLATION_UNAVAILABLE: "SESSION_SCOPE_ISOLATION_UNAVAILABLE",
11
+ PROCESS_ACTIVE: "SESSION_SCOPE_PROCESS_ACTIVE",
12
+ PARENT_UNAVAILABLE: "SESSION_SCOPE_PARENT_UNAVAILABLE",
13
+ STALE_WORKSPACE: "SESSION_SCOPE_STALE_WORKSPACE",
14
+ });
15
+ const DEFAULT_PATH_SERVICES = {
16
+ canonical: canonicalPath,
17
+ isDirectory(path) {
18
+ if (!existsSync(path))
19
+ return false;
20
+ try {
21
+ return statSync(path).isDirectory();
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ },
27
+ };
28
+ const MODES = new Set(["full", "focused", "isolated"]);
29
+ export class SessionScopeError extends Error {
30
+ code;
31
+ constructor(code, message) {
32
+ super(message);
33
+ this.name = "SessionScopeError";
34
+ this.code = code;
35
+ }
36
+ }
37
+ function comparisonKey(path, caseSensitive) {
38
+ return caseSensitive ? path : path.toLowerCase();
39
+ }
40
+ /** Drop duplicate and descendant roots already covered by a selected parent. */
41
+ export function collapseNestedRoots(roots, caseSensitive = process.platform !== "win32") {
42
+ const unique = new Map();
43
+ for (const root of roots) {
44
+ if (typeof root !== "string" || root.length === 0)
45
+ continue;
46
+ unique.set(comparisonKey(root, caseSensitive), root);
47
+ }
48
+ const candidates = [...unique.values()].sort((left, right) => left.length - right.length
49
+ || comparisonKey(left, caseSensitive).localeCompare(comparisonKey(right, caseSensitive)));
50
+ const result = [];
51
+ for (const candidate of candidates) {
52
+ if (result.some((root) => isLexicallyUnder(candidate, root, caseSensitive)))
53
+ continue;
54
+ result.push(candidate);
55
+ }
56
+ return result.sort((left, right) => comparisonKey(left, caseSensitive).localeCompare(comparisonKey(right, caseSensitive)));
57
+ }
58
+ /** Ancestors visible only for navigation from the workspace to content roots. */
59
+ export function navigationRootsFor(workspaceRoot, roots, caseSensitive = process.platform !== "win32") {
60
+ if (!workspaceRoot)
61
+ return [];
62
+ const ancestors = new Map();
63
+ for (const root of roots) {
64
+ if (!isLexicallyUnder(root, workspaceRoot, caseSensitive))
65
+ continue;
66
+ let current = dirname(root);
67
+ while (isLexicallyUnder(current, workspaceRoot, caseSensitive)) {
68
+ ancestors.set(comparisonKey(current, caseSensitive), current);
69
+ if (comparisonKey(current, caseSensitive) === comparisonKey(workspaceRoot, caseSensitive))
70
+ break;
71
+ const parent = dirname(current);
72
+ if (parent === current)
73
+ break;
74
+ current = parent;
75
+ }
76
+ }
77
+ const distanceFromWorkspace = (path) => {
78
+ let distance = 0;
79
+ let current = path;
80
+ while (comparisonKey(current, caseSensitive) !== comparisonKey(workspaceRoot, caseSensitive)) {
81
+ const parent = dirname(current);
82
+ if (parent === current)
83
+ return Number.MAX_SAFE_INTEGER;
84
+ distance += 1;
85
+ current = parent;
86
+ }
87
+ return distance;
88
+ };
89
+ return [...ancestors.values()].sort((left, right) => distanceFromWorkspace(left) - distanceFromWorkspace(right)
90
+ || comparisonKey(left, caseSensitive).localeCompare(comparisonKey(right, caseSensitive)));
91
+ }
92
+ /** Validate and canonicalize roots immediately before persisting a snapshot. */
93
+ export function normalizeSessionScopeRoots(input, workspaceRoot, options = {}) {
94
+ const { allowExternalRoots = false, caseSensitive = process.platform !== "win32", paths = DEFAULT_PATH_SERVICES, } = options;
95
+ if (!Array.isArray(input)) {
96
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.INVALID_ROOT, "Invalid session scope root list.");
97
+ }
98
+ if (input.length > MAX_ROOTS) {
99
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.INVALID_ROOT, `Session scope supports at most ${MAX_ROOTS} roots.`);
100
+ }
101
+ if (!isAbsolute(workspaceRoot) || !paths.isDirectory(workspaceRoot)) {
102
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.STALE_WORKSPACE, "The session workspace is unavailable.");
103
+ }
104
+ const lexicalWorkspace = resolve(workspaceRoot);
105
+ const canonicalWorkspace = paths.canonical(workspaceRoot);
106
+ const roots = [];
107
+ for (const candidate of input) {
108
+ if (typeof candidate !== "string" || !isAbsolute(candidate) || !paths.isDirectory(candidate)) {
109
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.INVALID_ROOT, "A selected session scope root is invalid or unavailable.");
110
+ }
111
+ const lexicalCandidate = resolve(candidate);
112
+ const canonicalCandidate = paths.canonical(candidate);
113
+ const lexicalInside = isLexicallyUnder(lexicalCandidate, lexicalWorkspace, caseSensitive);
114
+ const canonicalInside = isLexicallyUnder(canonicalCandidate, canonicalWorkspace, caseSensitive);
115
+ if (!allowExternalRoots && !lexicalInside && !canonicalInside) {
116
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.OUTSIDE_WORKSPACE, "A selected path is outside the session workspace.");
117
+ }
118
+ if (!allowExternalRoots && lexicalInside && !canonicalInside) {
119
+ throw new SessionScopeError(SESSION_SCOPE_ERROR.SYMLINK_ESCAPE, "A selected path resolves outside the session workspace.");
120
+ }
121
+ roots.push(canonicalCandidate);
122
+ }
123
+ return collapseNestedRoots(roots, caseSensitive);
124
+ }
125
+ function isScopeMode(value) {
126
+ return typeof value === "string" && MODES.has(value);
127
+ }
128
+ function latestScopeData(events) {
129
+ for (let index = events.length - 1; index >= 0; index -= 1) {
130
+ if (events[index]?.type === SESSION_SCOPE_EVENT)
131
+ return events[index]?.data;
132
+ }
133
+ return undefined;
134
+ }
135
+ function foldedRoots(roots, workspaceRoot, caseSensitive) {
136
+ if (!Array.isArray(roots) || !workspaceRoot)
137
+ return [];
138
+ const valid = roots.flatMap((root) => {
139
+ if (typeof root !== "string" || !isAbsolute(root))
140
+ return [];
141
+ const canonicalRoot = canonicalPath(root);
142
+ return isLexicallyUnder(canonicalRoot, workspaceRoot, caseSensitive)
143
+ ? [canonicalRoot]
144
+ : [];
145
+ });
146
+ return collapseNestedRoots(valid, caseSensitive);
147
+ }
148
+ /** Fold new durable state, falling back to the upstream event only for legacy sessions. */
149
+ export function effectiveSessionScope(events, sessionHeader = {}, caseSensitive = process.platform !== "win32") {
150
+ const data = latestScopeData(events);
151
+ const headerRoot = typeof sessionHeader.cwd === "string" ? sessionHeader.cwd : "";
152
+ const canonicalHeaderRoot = headerRoot ? canonicalPath(headerRoot) : "";
153
+ if (data !== undefined) {
154
+ const validSnapshot = data.version === 1 && isScopeMode(data.mode);
155
+ const mode = validSnapshot ? data.mode : "focused";
156
+ const eventRoot = typeof data.workspaceRoot === "string" ? data.workspaceRoot : "";
157
+ const canonicalEventRoot = eventRoot ? canonicalPath(eventRoot) : "";
158
+ const workspaceRoot = canonicalHeaderRoot || canonicalEventRoot;
159
+ if (mode === "full")
160
+ return { mode, workspaceRoot, roots: [], navigationRoots: [] };
161
+ // Malformed or stale persisted restrictions fail closed instead of widening
162
+ // the session back to the full workspace.
163
+ const stale = Boolean(canonicalHeaderRoot
164
+ && canonicalEventRoot
165
+ && comparisonKey(canonicalHeaderRoot, caseSensitive) !== comparisonKey(canonicalEventRoot, caseSensitive));
166
+ const roots = validSnapshot && !stale
167
+ ? foldedRoots(data.roots, workspaceRoot, caseSensitive)
168
+ : [];
169
+ return {
170
+ mode,
171
+ workspaceRoot,
172
+ roots,
173
+ navigationRoots: navigationRootsFor(workspaceRoot, roots, caseSensitive),
174
+ };
175
+ }
176
+ if (events.some((event) => event.type === SELECTION_EVENT)) {
177
+ const legacy = selectionOf(events);
178
+ const workspaceRoot = canonicalHeaderRoot
179
+ || (legacy.workspaceRoot ? canonicalPath(legacy.workspaceRoot) : "");
180
+ const roots = foldedRoots(legacy.roots, workspaceRoot, caseSensitive);
181
+ return {
182
+ mode: "focused",
183
+ workspaceRoot,
184
+ roots,
185
+ navigationRoots: navigationRootsFor(workspaceRoot, roots, caseSensitive),
186
+ };
187
+ }
188
+ return { mode: "full", workspaceRoot: canonicalHeaderRoot, roots: [], navigationRoots: [] };
189
+ }
190
+ export function createSessionScopeEvent(mode, roots, workspaceRoot, source, options) {
191
+ const canonicalWorkspaceRoot = (options?.paths ?? DEFAULT_PATH_SERVICES).canonical(workspaceRoot);
192
+ return {
193
+ version: 1,
194
+ mode,
195
+ workspaceRoot: canonicalWorkspaceRoot,
196
+ roots: mode === "full" ? [] : normalizeSessionScopeRoots(roots, workspaceRoot, options),
197
+ source,
198
+ };
199
+ }
200
+ //# sourceMappingURL=session-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scope.js","sourceRoot":"","sources":["../src/session-scope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { ScopeSession } from "./host-api.js";
2
+ import { type ScopedFilesystemOperation } from "./scope-visibility.js";
3
+ export interface ScopeToolExecution {
4
+ callId?: string;
5
+ rootCallId?: string;
6
+ token?: symbol;
7
+ parent?: symbol;
8
+ name: string;
9
+ arguments: unknown;
10
+ agent?: {
11
+ session?: ScopeSession;
12
+ };
13
+ signal?: AbortSignal;
14
+ }
15
+ export interface ScopeToolExecutionResult {
16
+ isError: boolean;
17
+ value?: unknown;
18
+ content: unknown[];
19
+ error?: unknown;
20
+ additionalContexts?: unknown[];
21
+ }
22
+ export interface ScopeToolDispatcher {
23
+ execute(execution: ScopeToolExecution): Promise<ScopeToolExecutionResult>;
24
+ }
25
+ export interface ScopeToolGuardOptions {
26
+ splitBroadSearches?: boolean;
27
+ isolatedBackendReady?: boolean;
28
+ sandboxMode?: string;
29
+ }
30
+ export interface ToolPathRequest {
31
+ path: string;
32
+ operation: ScopedFilesystemOperation;
33
+ }
34
+ export interface ScopeToolAdapter {
35
+ name: string;
36
+ extractPaths(argumentsValue: unknown): ToolPathRequest[];
37
+ }
38
+ export declare const DEFAULT_TOOL_ADAPTERS: readonly ScopeToolAdapter[];
39
+ export declare class ScopeToolAdapterRegistry {
40
+ private readonly adapters;
41
+ constructor(adapters?: readonly ScopeToolAdapter[]);
42
+ register(adapter: ScopeToolAdapter): () => void;
43
+ requests(execution: ScopeToolExecution): ToolPathRequest[];
44
+ }
45
+ /** Monotonic tool guard: undefined allows; a stable reason string denies. */
46
+ export declare function guardScopeToolExecution(execution: ScopeToolExecution, adapters: ScopeToolAdapterRegistry, fallbackWorkspaceRoot?: string, options?: ScopeToolGuardOptions): string | undefined;
47
+ /**
48
+ * Split a navigation-root glob/grep into child calls under content roots.
49
+ * Direct searches already inside one content root pass through unchanged.
50
+ */
51
+ export declare function dispatchScopedSearchExecution(execution: ScopeToolExecution, dispatcher: ScopeToolDispatcher, next: () => Promise<ScopeToolExecutionResult>, fallbackWorkspaceRoot?: string): Promise<ScopeToolExecutionResult>;
52
+ //# sourceMappingURL=tool-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-guard.d.ts","sourceRoot":"","sources":["../src/tool-guard.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kBAAkB,CAAC,EAAE,OAAO,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC3E;AAED,MAAM,WAAW,qBAAqB;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,cAAc,EAAE,OAAO,GAAG,eAAe,EAAE,CAAC;CAC1D;AA6CD,eAAO,MAAM,qBAAqB,EAAE,SAAS,gBAAgB,EAQ3D,CAAC;AAEH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;IAEhE,YAAY,QAAQ,GAAE,SAAS,gBAAgB,EAA0B,EAExE;IAED,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAQ9C;IAED,QAAQ,CAAC,SAAS,EAAE,kBAAkB,GAAG,eAAe,EAAE,CAEzD;CACF;AA+BD,6EAA6E;AAC7E,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,wBAAwB,EAClC,qBAAqB,SAAK,EAC1B,OAAO,GAAE,qBAA0B,GAClC,MAAM,GAAG,SAAS,CAwCpB;AA4DD;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,SAAS,EAAE,kBAAkB,EAC7B,UAAU,EAAE,mBAAmB,EAC/B,IAAI,EAAE,MAAM,OAAO,CAAC,wBAAwB,CAAC,EAC7C,qBAAqB,SAAK,GACzB,OAAO,CAAC,wBAAwB,CAAC,CA+BnC"}