@f5-sales-demo/xcsh 19.102.4 → 19.102.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "19.102.4",
4
+ "version": "19.102.6",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -57,13 +57,13 @@
57
57
  "dependencies": {
58
58
  "@agentclientprotocol/sdk": "1.3.0",
59
59
  "@mozilla/readability": "^0.6",
60
- "@f5-sales-demo/xcsh-stats": "19.102.4",
61
- "@f5-sales-demo/pi-agent-core": "19.102.4",
62
- "@f5-sales-demo/pi-ai": "19.102.4",
63
- "@f5-sales-demo/pi-natives": "19.102.4",
64
- "@f5-sales-demo/pi-resource-management": "19.102.4",
65
- "@f5-sales-demo/pi-tui": "19.102.4",
66
- "@f5-sales-demo/pi-utils": "19.102.4",
60
+ "@f5-sales-demo/xcsh-stats": "19.102.6",
61
+ "@f5-sales-demo/pi-agent-core": "19.102.6",
62
+ "@f5-sales-demo/pi-ai": "19.102.6",
63
+ "@f5-sales-demo/pi-natives": "19.102.6",
64
+ "@f5-sales-demo/pi-resource-management": "19.102.6",
65
+ "@f5-sales-demo/pi-tui": "19.102.6",
66
+ "@f5-sales-demo/pi-utils": "19.102.6",
67
67
  "@sinclair/typebox": "^0.34",
68
68
  "@xterm/headless": "^6.0",
69
69
  "ajv": "^8.20",
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "19.102.4",
21
- "commit": "5beee843e5cd185c015cf2ca04edf5dde54eadb8",
22
- "shortCommit": "5beee84",
20
+ "version": "19.102.6",
21
+ "commit": "983ca4f8d682745db27c890d634cb459fc7e3e32",
22
+ "shortCommit": "983ca4f",
23
23
  "branch": "main",
24
- "tag": "v19.102.4",
25
- "commitDate": "2026-07-30T20:21:22Z",
26
- "buildDate": "2026-07-30T20:45:02.127Z",
24
+ "tag": "v19.102.6",
25
+ "commitDate": "2026-07-31T01:44:23Z",
26
+ "buildDate": "2026-07-31T02:07:47.614Z",
27
27
  "dirty": true,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5-sales-demo/xcsh",
30
30
  "repoSlug": "f5-sales-demo/xcsh",
31
- "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/5beee843e5cd185c015cf2ca04edf5dde54eadb8",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.102.4"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/983ca4f8d682745db27c890d634cb459fc7e3e32",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.102.6"
33
33
  };
@@ -1,15 +1,8 @@
1
1
  <workspace-boundary>
2
- This session may be scoped to a single customer. Treat the working directory as the scope of the
3
- work: what the task needs is inside it, and another customer's material is not yours to open here.
4
- - You **MUST NOT** range across the filesystem hunting for files, examples, or precedent. Search
5
- within the working directory. Your own skills and plugins, and paths the operator granted
6
- explicitly (`--allow-path`, `sandbox.allowRead`), are legitimate and are not licence to browse
7
- anywhere else.
8
- - Subdirectories may themselves be separate customers. Reachable does not mean in scope: work in
9
- the one the task names, state the crossing when the task genuinely spans more than one, and
10
- **MUST NOT** merge two customers' material into one artifact.
11
- - A sandbox confines this session's file access, but its boundary is the working directory, not the
12
- customer subdirectory: nothing refuses a sibling, so the rule above is yours to keep. When the
13
- sandbox is active a path outside the working directory is refused — that is the boundary working,
14
- not a tool failure. Say what you needed and why, and **MUST NOT** reach the same path another way.
2
+ Separate tenants may sit side by side as subdirectories of the working directory, and anywhere
3
+ else you can reach when filesystem isolation is off. Reaching something is not the same as it being
4
+ in scope; keeping tenants apart is your judgment.
5
+ - Work in the one the task names, and say so when the task genuinely spans more than one.
6
+ - You **MUST NOT** open another tenant's material for context, examples, or precedent when the task
7
+ did not ask for it, and **MUST NOT** merge two tenants' material into one artifact.
15
8
  </workspace-boundary>
@@ -203,6 +203,12 @@ const COMMAND_BEARING_CONFIG = [
203
203
  // was measured working with its config.yml read-only.
204
204
  path.join(".config", "glab-cli", "aliases.yml"),
205
205
  path.join("Library", "Application Support", "glab-cli", "aliases.yml"),
206
+ // Build configuration that redirects a later build. Protected by *omission* while home was denied —
207
+ // the cache carve-outs grant `.cargo/registry` rather than `.cargo` — so #2637 had to name them. A
208
+ // write here is persistence, not theft: the operator's next build runs what it says. Reads are fine.
209
+ path.join(".cargo", "config.toml"),
210
+ path.join(".gradle", "init.gradle"),
211
+ path.join(".gradle", "init.d"),
206
212
  ];
207
213
 
208
214
  /** Read-only inside home: configuration a tool needs to behave correctly, but must not rewrite. */
@@ -268,9 +274,10 @@ const AGENT_PROFILE_FILES = ["user-profile.json", "computer-profile.json", "sett
268
274
  * unforeseen — `/data`, `/scratch`, a bespoke mount.
269
275
  */
270
276
  const DATA_ROOTS = [
271
- "/Users", // macOS home container: other operators' accounts, /Users/Shared
272
- "/home", // Linux home container
273
- "/root", // Linux superuser home
277
+ // `/Users` and `/home` are deliberately absent: denying the home container denies this operator's own
278
+ // home with it, which is the whole of #2637. Other accounts are 0700, so the filesystem already refuses
279
+ // them; the fence is not here to re-implement file permissions.
280
+ "/root", // Linux superuser home: not this operator's account
274
281
  "/Volumes", // macOS mounts. Per-container, not per-child: /Volumes/Macintosh HD resolves to /,
275
282
  "/mnt", // which `tooBroadToDeny` then rejects, and the kernel resolves such a path before any
276
283
  "/media", // rule matches it — so denying the container cannot deny the boot volume.
@@ -528,8 +535,22 @@ export function buildContainmentFence(options: ContainmentOptions): ContainmentF
528
535
  // rejects, so `/`, `/usr`, `/tmp` and `$TMPDIR` are never denied and operational paths stay
529
536
  // reachable. And it costs nothing below: the workspace is allowed at greater depth, and
530
537
  // `fenceVerdict` takes the deepest match.
531
- if (home !== undefined && home !== workspace) deny.add(home);
538
+ // Home is never denied, and the walk stops there (#2637).
539
+ //
540
+ // This is a professional courtesy, not a prison: an effort to stop *inadvertent* filesystem wandering
541
+ // between customers, for an operator with senior technical skills and the same rights on this machine
542
+ // as the agent acting for them. Denying home outright refused `~/git/STYLE_GUIDE.md` and then needed
543
+ // ~30 carve-outs to make ordinary tooling work again — every one of them evidence the posture was wrong.
544
+ //
545
+ // What the walk still covers is the part that matters: every level between the workspace and home. With
546
+ // the workspace at `~/MEDDPICC/CUSTOMER-A`, `~/MEDDPICC` is denied so the `CUSTOMER-B` sibling is unreachable,
547
+ // and with `<container>/<tenant>/repo` every level up to home is denied, so the v19.100.1 cross-tenant
548
+ // regression does not return.
549
+ //
550
+ // A session whose folder IS home, or a direct child of it, therefore fences nothing above itself. That
551
+ // is deliberate: if the operator chooses to work in home, that is their filesystem to lay out.
532
552
  for (let ancestor = path.dirname(workspace); ; ancestor = path.dirname(ancestor)) {
553
+ if (ancestor === home) break; // the operator's own account is theirs
533
554
  if (tooBroadToDeny(ancestor, fsRoot)) break;
534
555
  deny.add(ancestor);
535
556
  const next = path.dirname(ancestor);
@@ -599,7 +620,7 @@ export function buildContainmentFence(options: ContainmentOptions): ContainmentF
599
620
  }
600
621
 
601
622
  // Top-level directories that hold somebody's files. Without these the fence covered only home and
602
- // the workspace's ancestors, so with the workspace at `~/MEDDPICC/EQUIFAX` another operator's
623
+ // the workspace's ancestors, so with the workspace at `~/MEDDPICC/CUSTOMER-A` another operator's
603
624
  // account, an external volume and `/data/globex` were all readable AND writable — measured. The
604
625
  // command-text scan was refusing them on the way in, which is exactly why that scan could not
605
626
  // simply be stood down (#2624).
@@ -629,6 +650,11 @@ export function buildContainmentFence(options: ContainmentOptions): ContainmentF
629
650
  // e.g. /Volumes/Macintosh HD -> /. Skipped for the whole-root list, per the note above.
630
651
  if (!rootScoped.has(resolved) && tooBroadToDeny(resolved, fsRoot)) continue;
631
652
  if (resolved === fsRoot) continue; // never the root the workspace lives on
653
+ // Never a directory that CONTAINS home, because denying it denies home (#2637). Removing `/Users`
654
+ // and `/home` from the static list was not enough: the root enumeration re-adds them, since
655
+ // `Users` is not an operational name. That is what still refused `~/git/STYLE_GUIDE.md` at the
656
+ // kernel while `fenceVerdict` said allow — the unit tests use synthetic roots and could not see it.
657
+ if (home !== undefined && pathIsWithin(resolved, home)) continue;
632
658
  // A deny beats an allow at EQUAL depth, so denying a root that IS the workspace or IS something
633
659
  // the operator granted would not be redundant — it would silently revoke the grant. Deeper is
634
660
  // fine and intended: an ancestor deny with the workspace allowed inside it is the normal shape.