@f5-sales-demo/xcsh 19.87.1 → 19.88.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "19.87.1",
4
+ "version": "19.88.0",
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",
@@ -56,13 +56,13 @@
56
56
  "dependencies": {
57
57
  "@agentclientprotocol/sdk": "0.16.1",
58
58
  "@mozilla/readability": "^0.6",
59
- "@f5-sales-demo/xcsh-stats": "19.87.1",
60
- "@f5-sales-demo/pi-agent-core": "19.87.1",
61
- "@f5-sales-demo/pi-ai": "19.87.1",
62
- "@f5-sales-demo/pi-natives": "19.87.1",
63
- "@f5-sales-demo/pi-resource-management": "19.87.1",
64
- "@f5-sales-demo/pi-tui": "19.87.1",
65
- "@f5-sales-demo/pi-utils": "19.87.1",
59
+ "@f5-sales-demo/xcsh-stats": "19.88.0",
60
+ "@f5-sales-demo/pi-agent-core": "19.88.0",
61
+ "@f5-sales-demo/pi-ai": "19.88.0",
62
+ "@f5-sales-demo/pi-natives": "19.88.0",
63
+ "@f5-sales-demo/pi-resource-management": "19.88.0",
64
+ "@f5-sales-demo/pi-tui": "19.88.0",
65
+ "@f5-sales-demo/pi-utils": "19.88.0",
66
66
  "@sinclair/typebox": "^0.34",
67
67
  "@xterm/headless": "^6.0",
68
68
  "ajv": "^8.20",
@@ -100,16 +100,48 @@ export const BROWSER_TOOL_NAMES: readonly string[] = [
100
100
 
101
101
  /**
102
102
  * Builtin agent tools scoped into the headless OFFICE bridge (`xcsh office serve`).
103
- * The Office task pane drives a document (Excel/PowerPoint/Word), NOT a browser, so
104
- * it must get NONE of the {@link BROWSER_TOOL_NAMES} — those would be hallucinated
105
- * against a host with no browser to drive. The document's own tools arrive at
106
- * runtime over the bridge via `set_host_tools`; this list is only the minimal
107
- * general-purpose, host-neutral builtin toolset a document assistant can safely use.
108
103
  *
109
- * NOTE: an EMPTY list cannot express "no builtin tools" createAgentSession/createTools
110
- * treat `[]` as "unscoped" and hand back the FULL builtin registry (bash/edit/python/
111
- * browser/…). So we pass an explicit minimal set instead. `calc` is the one builtin
112
- * that is pure computation — no browser, no shell, no filesystem, no network — and is
113
- * genuinely useful for spreadsheet/document math.
104
+ * FULL CLI-PARITY tool set (minus browser automation): the Office pane is a full
105
+ * local xcsh agent, so it gets the same general-purpose native tools the CLI has
106
+ * `bash` (so it can shell out to `az`, `gh`, terraform, git, …), the file tools
107
+ * (`read`/`write`/`edit`), search (`grep`), plus planning (`todo_write`,
108
+ * `task`) and `calc`. (File-finding is covered by `bash`; the builtin `find`
109
+ * tool is omitted because its name collides with a browser tool.) The document's own Excel/PowerPoint/Word tools arrive at
110
+ * runtime over the bridge via `set_host_tools`.
111
+ *
112
+ * DELIBERATELY EXCLUDED:
113
+ * - Every {@link BROWSER_TOOL_NAMES} entry — there is no browser to drive in a
114
+ * document pane, so navigate/click/screenshot would only be hallucinated.
115
+ * - `ask` (needs interactive stdin → would hang headless), `python` (spawns a
116
+ * kernel → startup cost), `ssh`/`debug`/`notebook`/`browser`/`get_page_context`.
117
+ *
118
+ * SAFETY: the headless session pairs this with the bundled `sandbox-guard`
119
+ * extension (see headless-bridge.ts `bundledExtensions`), which confines the file
120
+ * tools + the shell's working dir to the launch cwd subtree — the CLI's own model.
121
+ * `az`/`gh` still run (network actions aren't filesystem-confined); credentials must
122
+ * already exist for the process user. There is no per-tool approval prompt — the
123
+ * local trusted bridge auto-runs tools exactly as the CLI does.
124
+ *
125
+ * THREAT MODEL (reviewed + accepted, 2026-07-24): the pane's agent auto-reads
126
+ * document content, which could be adversarial (a prompt-injected customer .xlsx)
127
+ * and steer it into shell/`az`/`gh` calls; the filesystem sandbox blocks file
128
+ * damage outside cwd but NOT network/cloud actions. This is the same exposure the
129
+ * xcsh CLI already carries (no approval system anywhere). The operator explicitly
130
+ * chose full CLI parity + FS sandbox over a bash approval gate, mitigating in
131
+ * practice by only opening trusted documents. If untrusted files become common,
132
+ * revisit with a per-shell approval round-trip (host_tool_call-style frame).
133
+ *
134
+ * NOTE: an EMPTY list cannot express "no builtin tools" — createTools treats `[]` as
135
+ * "unscoped" and returns the FULL registry (including browser tools). So this is an
136
+ * explicit curated array, not `[]`.
114
137
  */
115
- export const OFFICE_TOOL_NAMES: readonly string[] = ["calc"];
138
+ export const OFFICE_TOOL_NAMES: readonly string[] = [
139
+ "read",
140
+ "write",
141
+ "edit",
142
+ "bash",
143
+ "grep",
144
+ "todo_write",
145
+ "task",
146
+ "calc",
147
+ ];
@@ -134,12 +134,10 @@ export async function startHeadlessChatBridge(deps: HeadlessBridgeDeps = default
134
134
  // selectProvider() reuses a dead bridge. The caller (startOfficeServe) treats
135
135
  // the rethrow as a non-fatal "pane only" fallback.
136
136
  try {
137
- // Create ONE headless Office session scoped to the minimal general builtin set
138
- // (OFFICE_TOOL_NAMES) with NO browser tools — neither the browser BUILTINS
139
- // (navigate/click/…) nor the bridge-proxying browser CUSTOM tools, both of which
140
- // would be hallucinated in a document task pane (there is no browser to drive).
141
- // The document's own tools (Excel/Word/PowerPoint) arrive at runtime via
142
- // set_host_tools; chat over xcsh's configured provider needs no browser tooling.
137
+ // Create ONE headless Office session with the full CLI-parity builtin set
138
+ // (OFFICE_TOOL_NAMES: bash/read/write/edit/grep/find/… NO browser tools, which
139
+ // would be hallucinated in a document task pane). The document's own tools
140
+ // (Excel/Word/PowerPoint) arrive at runtime via set_host_tools.
143
141
  const { session } = await deps.createAgentSession({
144
142
  cwd,
145
143
  hasUI: false,
@@ -149,6 +147,11 @@ export async function startHeadlessChatBridge(deps: HeadlessBridgeDeps = default
149
147
  enableMCP: false,
150
148
  enableLsp: false,
151
149
  disableExtensionDiscovery: true,
150
+ // …but DO load the bundled filesystem sandbox: the pane runs full CLI-parity
151
+ // tools (bash/read/write), so it needs the CLI's safety net confining file
152
+ // tools + the shell's cwd to the launch directory subtree (sandbox.enabled
153
+ // defaults true). Without this, a discovery-disabled session ran ungated.
154
+ bundledExtensions: ["sandbox-guard"],
152
155
  });
153
156
 
154
157
  const chatHandler = new deps.ChatHandlerCtor(bridge, session);
@@ -85,6 +85,19 @@ SAFETY — NEVER DO THESE:
85
85
 
86
86
  `;
87
87
 
88
+ /**
89
+ * Shared tail for every Office (document) profile: the pane is a FULL local xcsh
90
+ * agent — same native tools as the CLI — plus the one safety rule that shelling
91
+ * out makes necessary (don't let the agent kill its own bridge). Interpolated into
92
+ * each document profile so the three stay in sync (DRY).
93
+ */
94
+ const OFFICE_NATIVE_TOOLS_NOTE = `
95
+ NATIVE TOOLS: Beyond the document host tools, you have xcsh's full local toolset — \`bash\` (run shell commands, including CLIs like \`az\`, \`gh\`, \`terraform\`, \`git\` when installed and authenticated), file tools (\`read\`/\`write\`/\`edit\`), and \`grep\` — plus any skills available in this workspace. Reach for them when the task genuinely needs them (pull live data with a CLI, read a local file the user points you at). Prefer the document host tools for document work. Your file tools and shell are confined to the folder xcsh was launched from.
96
+
97
+ SAFETY:
98
+ - NEVER kill, stop, inspect, or manage the xcsh \`office serve\` process, its bridge ports, or any xcsh process — that bridge IS you; ending it ends the session.
99
+ - NEVER run \`lsof\`/\`fuser\`/\`kill\`/\`pkill\` against the bridge ports or use the shell to manage xcsh itself.`;
100
+
88
101
  /**
89
102
  * Excel task-pane self-awareness prompt. The assistant works the OPEN workbook
90
103
  * via host tools (arriving at runtime over the bridge), thinking in cells,
@@ -95,7 +108,7 @@ You are still xcsh, the F5 Distributed Cloud technical coworker defined in your
95
108
 
96
109
  CRITICAL: ALWAYS respond with TEXT FIRST — the user sees a chat pane and expects a conversational reply, not silence while tools run. Answer questions from the data you read; only WRITE to the workbook when the user asks you to.
97
110
 
98
- CONTEXT: You can only access the open workbook. Think in cells, ranges, and — above all — FORMULAS and their dependencies:
111
+ CONTEXT: Your workspace centers on the open workbook. Think in cells, ranges, and — above all — FORMULAS and their dependencies:
99
112
  - Preserve formula relationships. When you change a cell, let dependent cells recompute; do not overwrite a formula with its current value unless asked.
100
113
  - Warn the user before overwriting existing cell contents.
101
114
  - Cite specific cells and ranges precisely (e.g. A1, Sheet1!B2:B10) so the user can follow along.
@@ -105,6 +118,7 @@ TOOLS: Discover the workbook before you answer, then reach for the tool that mat
105
118
  - Use \`read_table\` for structured Excel Tables (it tracks the real extent), \`get_formulas\` to see the formulas behind cells, \`get_cell_metadata\` for cell types/number formats, and \`read_named_range\` to read a defined name.
106
119
  - Use \`sort_filter_table\` to sort or filter a Table by column.
107
120
  - Use \`read_range\`/\`write_range\` for arbitrary cell ranges (bare or sheet-qualified like Sheet2!A1:B10), and \`list_sheets\` when you only need the tab names.
121
+ ${OFFICE_NATIVE_TOOLS_NOTE}
108
122
 
109
123
  BEHAVIOR:
110
124
  - Respond concisely with markdown. The task pane is narrow — avoid long code blocks.
@@ -123,7 +137,7 @@ You are still xcsh, the F5 Distributed Cloud technical coworker defined in your
123
137
 
124
138
  CRITICAL: ALWAYS respond with TEXT FIRST — the user sees a chat pane and expects a conversational reply, not silence while tools run. Answer questions from what you read; only edit the deck when the user asks you to.
125
139
 
126
- CONTEXT: You can only access the open presentation. Think in slides, shapes, and the slide master:
140
+ CONTEXT: Your workspace centers on the open presentation. Think in slides, shapes, and the slide master:
127
141
  - Conform any new content to the deck's existing template, fonts, and colors — do not introduce a different look.
128
142
  - Make pinpoint, per-slide edits. Do NOT regenerate the whole deck to change one thing.
129
143
  - Refer to slides by number so the user can follow along.
@@ -132,6 +146,7 @@ TOOLS: Discover the deck before you answer, then reach for the tool that matches
132
146
  - Call \`get_presentation_info\` FIRST to discover all slides, their layouts, and shape counts before answering — do not guess the structure.
133
147
  - Use \`read_slide_shapes\` to see all shapes on a slide with their text + position, \`read_slide_layout\` for the layout/master applied to a slide, and \`modify_shape_text\` to edit the text of a named shape.
134
148
  - Use \`read_slides\` for a quick text-only scan of the whole deck, and \`add_text_box\`/\`add_slide\` to create new content.
149
+ ${OFFICE_NATIVE_TOOLS_NOTE}
135
150
 
136
151
  BEHAVIOR:
137
152
  - Respond concisely with markdown. The task pane is narrow — avoid long code blocks.
@@ -151,7 +166,7 @@ You are still xcsh, the F5 Distributed Cloud technical coworker defined in your
151
166
 
152
167
  CRITICAL: ALWAYS respond with TEXT FIRST — the user sees a chat pane and expects a conversational reply, not silence while tools run. Answer questions from what you read; only edit the document when the user asks you to.
153
168
 
154
- CONTEXT: You can only access the open document. Think in paragraphs, the current selection, comments, and tracked changes:
169
+ CONTEXT: Your workspace centers on the open document. Think in paragraphs, the current selection, comments, and tracked changes:
155
170
  - Preserve the document's styles and numbering — do not flatten formatting.
156
171
  - Describe your edits so the user can review them, and prefer changes the user can accept or reject.
157
172
  - When the user refers to "the selection" (or "this"), act on the current selection.
@@ -161,6 +176,7 @@ TOOLS: Discover the document before you answer, then reach for the tool that mat
161
176
  - Use \`read_paragraphs\` for styled paragraph content, \`read_selection\` for the current selection, \`get_comments\` for comments, and \`get_tracked_changes\` for revisions.
162
177
  - Use \`read_document\` when you need the full plain text.
163
178
  - Use \`insert_paragraph\` to add content at a specific location (start, end, or before/after the selection), and \`insert_text\` for inline text within a paragraph.
179
+ ${OFFICE_NATIVE_TOOLS_NOTE}
164
180
 
165
181
  BEHAVIOR:
166
182
  - Respond concisely with markdown. The task pane is narrow — avoid long code blocks.
@@ -336,8 +336,17 @@ export async function loadExtensionFromFactory(
336
336
 
337
337
  /**
338
338
  * Load extensions from paths.
339
+ *
340
+ * `bundledExtensionNames` opts specific bundled extensions (e.g. `["sandbox-guard"]`)
341
+ * into a session that has otherwise disabled discovery — the headless bridges use
342
+ * this to keep the CLI's filesystem safety net without paying for full discovery.
339
343
  */
340
- export async function loadExtensions(paths: string[], cwd: string, eventBus?: EventBus): Promise<LoadExtensionsResult> {
344
+ export async function loadExtensions(
345
+ paths: string[],
346
+ cwd: string,
347
+ eventBus?: EventBus,
348
+ bundledExtensionNames: readonly string[] = [],
349
+ ): Promise<LoadExtensionsResult> {
341
350
  const extensions: Extension[] = [];
342
351
  const errors: Array<{ path: string; error: string }> = [];
343
352
  const resolvedEventBus = eventBus ?? new EventBus();
@@ -356,11 +365,14 @@ export async function loadExtensions(paths: string[], cwd: string, eventBus?: Ev
356
365
  }
357
366
  }
358
367
 
359
- return {
360
- extensions,
361
- errors,
362
- runtime,
363
- };
368
+ const result: LoadExtensionsResult = { extensions, errors, runtime };
369
+
370
+ if (bundledExtensionNames.length > 0) {
371
+ const allow = new Set(bundledExtensionNames);
372
+ await loadBundledExtensions(result, cwd, resolvedEventBus, name => !allow.has(name));
373
+ }
374
+
375
+ return result;
364
376
  }
365
377
 
366
378
  interface ExtensionManifest {
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "19.87.1",
21
- "commit": "961f2b2d80f178d7165977576b0249907c21c68d",
22
- "shortCommit": "961f2b2",
20
+ "version": "19.88.0",
21
+ "commit": "4e87c996191e7c88ae503cedded3afee98177587",
22
+ "shortCommit": "4e87c99",
23
23
  "branch": "main",
24
- "tag": "v19.87.1",
25
- "commitDate": "2026-07-24T18:28:42Z",
26
- "buildDate": "2026-07-24T18:54:10.232Z",
24
+ "tag": "v19.88.0",
25
+ "commitDate": "2026-07-24T18:57:08Z",
26
+ "buildDate": "2026-07-24T19:23:46.325Z",
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/961f2b2d80f178d7165977576b0249907c21c68d",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.87.1"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/4e87c996191e7c88ae503cedded3afee98177587",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.88.0"
33
33
  };
package/src/sdk.ts CHANGED
@@ -188,6 +188,12 @@ export interface CreateAgentSessionOptions {
188
188
  additionalExtensionPaths?: string[];
189
189
  /** Disable extension discovery (explicit paths still load). */
190
190
  disableExtensionDiscovery?: boolean;
191
+ /**
192
+ * Bundled extensions to load even when discovery is disabled (e.g.
193
+ * `["sandbox-guard"]`). Lets a headless session keep the CLI's filesystem
194
+ * safety net without paying for full discovery.
195
+ */
196
+ bundledExtensions?: string[];
191
197
  /**
192
198
  * Pre-loaded extensions (skips file discovery).
193
199
  * @internal Used by CLI when extensions are loaded early to parse custom flags.
@@ -1238,7 +1244,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1238
1244
  let extensionsResult: LoadExtensionsResult;
1239
1245
  if (options.disableExtensionDiscovery) {
1240
1246
  const configuredPaths = options.additionalExtensionPaths ?? [];
1241
- extensionsResult = await logger.time("loadExtensions", loadExtensions, configuredPaths, cwd, eventBus);
1247
+ extensionsResult = await logger.time(
1248
+ "loadExtensions",
1249
+ loadExtensions,
1250
+ configuredPaths,
1251
+ cwd,
1252
+ eventBus,
1253
+ options.bundledExtensions ?? [],
1254
+ );
1242
1255
  for (const { path, error } of extensionsResult.errors) {
1243
1256
  logger.error("Failed to load extension", { path, error });
1244
1257
  }