@f5-sales-demo/xcsh 22.1.2 → 22.2.1

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.
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "22.1.2",
21
- "commit": "65d611cfa87b2269f14410aab45331c1701ecf7d",
22
- "shortCommit": "65d611c",
20
+ "version": "22.2.1",
21
+ "commit": "36bd12837ef237223ef118e02b72b46534defe70",
22
+ "shortCommit": "36bd128",
23
23
  "branch": "main",
24
- "tag": "v22.1.2",
25
- "commitDate": "2026-09-27T23:52:20Z",
26
- "buildDate": "2026-09-28T00:35:39.460Z",
24
+ "tag": "v22.2.1",
25
+ "commitDate": "2026-09-28T02:56:41Z",
26
+ "buildDate": "2026-09-28T03:32:41.148Z",
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/65d611cfa87b2269f14410aab45331c1701ecf7d",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v22.1.2"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/36bd12837ef237223ef118e02b72b46534defe70",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v22.2.1"
33
33
  };
@@ -40,14 +40,14 @@ export const EMBEDDED_DOCS: Readonly<Record<string, string>> = {
40
40
  "en/getting-started/first-manifest.mdx": "---\ntitle: \"Complete your first manifest lifecycle\"\ndescription: \"Validate, compare, apply, inspect, export, and delete one resource manifest.\"\nsidebar:\n order: 5\n label: \"First manifest\"\nhead:\n - tag: style\n content: |\n .sl-markdown-content .expressive-code { max-width: 100%; overflow-x: auto; }\n .sl-markdown-content table { display: block; max-width: 100%; overflow-x: auto; }\n---\n\nA manifest names one F5 Distributed Cloud resource with `kind`, `metadata`, and `spec`. Begin from an exported or catalog-backed example for the exact resource kind; do not invent schema fields. This page walks one resource from local validation to proven deletion, so run it against a lab namespace you are authorized to change.\n\n## How do I check a manifest without credentials?\n\n```bash\nxcsh validate -f manifest.yaml --result-file validation.json\n```\n\nValidation reads the file locally and returns a nonzero status for malformed or unsupported input. Review the aggregate JavaScript Object Notation (JSON) report before moving to a live context.\n\n## How do I preview the live change?\n\n```bash\nxcsh diff -f manifest.yaml --result-file diff.json\nxcsh apply -f manifest.yaml --dry-run client --result-file dry-run.json\n```\n\nA diff requires the intended context. A dry run calculates the mutation path without applying it. Read every create, update, and preserve entry before you continue.\n\n## How do I apply and verify the resource?\n\n```bash\nxcsh apply -f manifest.yaml --result-file apply.json\nxcsh get <KIND> <NAME> -n <NAMESPACE> --result-file get.json\n```\n\nCompare the identity and the fields you set in `get.json` against the manifest. A zero exit status records that the request was accepted; only the retrieved object records the resulting state.\n\n## How do I export and remove it?\n\n```bash\nxcsh export <KIND> <NAME> -n <NAMESPACE> -f exported.yaml\nxcsh delete <KIND> <NAME> -n <NAMESPACE> --result-file delete.json\nxcsh get <KIND> <NAME> -n <NAMESPACE>\n```\n\nSanitize server-managed metadata out of `exported.yaml` before you store it. The closing `get` must return the not-found response for that kind; a delete request without the absence check leaves the lifecycle unfinished.\n",
41
41
  "en/getting-started/index.mdx": "---\ntitle: \"Getting started\"\ndescription: \"Install xcsh, connect a model and an F5 Distributed Cloud tenant, and complete a safe first workflow.\"\nsidebar:\n order: 0\n label: \"Overview\"\n---\n\nUse this section to reach the first useful command without configuring every optional integration.\n\n## What do I need before I begin?\n\nYou need a supported xcsh build and one model provider. F5 Distributed Cloud credentials are needed only for live resource commands; local manifest validation works without them.\n\n## Which path matches my goal?\n\n1. [Install, update, or remove xcsh](/xcsh/en/#install-xcsh).\n2. [Authenticate a model](/xcsh/en/getting-started/model-authentication/), which the quickstart requires.\n3. Run the [general quickstart](/xcsh/en/#quickstart).\n4. Connect an [F5 Distributed Cloud context](/xcsh/en/f5-distributed-cloud/contexts-namespaces/) when you intend to reach a tenant.\n5. Complete [your first manifest lifecycle](/xcsh/en/getting-started/first-manifest/) against a lab namespace you are authorized to change.\n",
42
42
  "en/getting-started/model-authentication.mdx": "---\ntitle: \"Authenticate a model provider\"\ndescription: \"Configure an API key or supported subscription login without placing credentials in project files.\"\nsidebar:\n order: 2\n label: \"Model authentication\"\nhead:\n - tag: style\n content: |\n .sl-markdown-content .expressive-code { max-width: 100%; overflow-x: auto; }\n .sl-markdown-content table { display: block; max-width: 100%; overflow-x: auto; }\n---\n\nModel credentials belong in the provider-supported environment or subscription login, not in prompts, settings committed to a repository, or command arguments.\n\n## Which authentication method should I choose?\n\nUse a provider application programming interface (API) key for service-style automation. Use a supported subscription login for an interactive user session. Run `xcsh --list-models <PROVIDER>` after authentication to confirm the credential can enumerate models.\n\n## How do I select a model explicitly?\n\n```bash\nxcsh --model p-openai/gpt-5.2 --no-tools -p \"Summarize README.md\"\n```\n\nReplace the example with a model shown by your installed build. Provider-qualified names avoid an accidental fuzzy match when several providers expose similar model names.\n\nFor Anthropic, either complete `/login anthropic` with a Claude subscription or export\n`ANTHROPIC_API_KEY` for direct API billing. `--model fable` and `--model anthropic/fable` are exact\naliases for `anthropic/claude-fable-5-1`; use `anthropic/claude-fable-5` when you specifically need\nthe earlier Fable 5 model. Fable access depends on the selected account's entitlement.\n\nStart xcsh, enter `/login`, and choose only the provider you intend to use. Complete the\nprovider-owned sign-in flow, return to the model picker, and select an explicit model.\nAuthentication may store a refresh token or subscription session outside the repository; a prompt\nnever needs the credential value. Send a no-tool prompt, inspect the selected provider and model,\nthen use `/logout` when the account must not remain available on that workstation.\n",
43
- "en/index.mdx": "---\ntitle: xcsh\ndescription: Install xcsh, authenticate a model provider, and begin a reviewable terminal or F5 Distributed Cloud workflow.\ntemplate: splash\neditUrl: false\nsidebar:\n hidden: true\n order: 0\nhead:\n - tag: style\n content: |\n main:has(.landing-hero) > .content-panel:first-of-type { padding-block-end: .5rem; }\n main:has(.landing-hero) > .content-panel:first-of-type + .content-panel { border-top: 0; padding-block-start: 0; }\n .landing-hero { display: grid; gap: clamp(2rem, 4vw, 4rem); grid-template-columns: minmax(0, 1fr) minmax(18rem, 35rem); align-items: center; margin-block: .75rem 2rem; }\n .landing-hero__content, .landing-hero__image, .landing-installer, .landing-installer .expressive-code, .landing-command { min-width: 0; }\n h1#_top { max-width: 13ch; margin-block-end: 0; text-wrap: balance; }\n .landing-hero__lede { max-width: 42rem; color: var(--sl-color-gray-2); font-size: var(--sl-text-lg); }\n .landing-installer { margin-block: 0 4rem; padding: clamp(1rem, 2vw, 1.5rem); border: 1px solid var(--sl-color-gray-5); border-radius: .75rem; background: var(--sl-color-bg-nav); }\n .landing-installer h2 { margin: 0; font-size: var(--sl-text-lg); }\n .landing-installer p { margin-block: .5rem 0; }\n .landing-installer pre { max-width: 100%; overflow-x: auto; }\n .landing-installer .expressive-code .ec-line .code { padding-inline-end: 3.5rem; white-space: pre; overflow-wrap: normal; }\n .landing-installer .expressive-code .copy button { opacity: 1; }\n .landing-hero__image { margin: 0; }\n .landing-hero__image img { display: block; width: 100%; height: auto; aspect-ratio: 1442 / 1268; object-fit: contain; filter: drop-shadow(0 1rem 2.5rem color-mix(in srgb, var(--sl-color-accent) 28%, transparent)); }\n .landing-grid { display: grid; gap: 1rem; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-block: 1.5rem; }\n .landing-explore { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n details.landing-disclosure { margin-block: 1rem 0; padding: .75rem 1rem; border: 1px solid var(--sl-color-gray-5); border-radius: .5rem; background: var(--sl-color-bg-nav); }\n details.landing-disclosure summary { cursor: pointer; font-weight: 600; }\n :is(.landing-hero, .landing-installer, .landing-grid, .landing-disclosure) :is(a, button, summary):focus-visible { outline: 2px solid var(--sl-color-accent-high); outline-offset: 3px; }\n @media (max-width: 50rem) { .landing-hero { grid-template-columns: minmax(0, 1fr); gap: 2rem; margin-block: .5rem 2rem; } .landing-installer { margin-block-end: 3rem; padding: 1rem; } .landing-grid { grid-template-columns: minmax(0, 1fr); } }\n---\n\nimport { Aside, Card, LinkCard, TabItem, Tabs } from '@astrojs/starlight/components';\n\n<section class=\"landing-hero\" aria-labelledby=\"_top\">\n <div class=\"landing-hero__content\">\n <p class=\"landing-hero__lede\">An AI-native terminal assistant for engineering, automation, and reviewable F5 Distributed Cloud workflows.</p>\n <p>xcsh is an independent open-source project derived from pi/pi-mono, specialized for F5 Distributed Cloud workflows; it is not an F5 product.</p>\n </div>\n\n <figure class=\"landing-hero__image\">\n <img src=\"/xcsh/images/xcsh-ghostty-terminal.png\" width=\"1442\" height=\"1268\" alt=\"xcsh running in a Ghostty terminal with the model selector, prompt, and session status visible.\" />\n </figure>\n</section>\n\n<section class=\"landing-installer\" aria-labelledby=\"install-xcsh\">\n\n## Install xcsh\n\n <p>Universal installer for macOS and Linux.</p>\n\n ```bash\n curl -fsSL https://raw.githubusercontent.com/f5-sales-demo/xcsh/main/scripts/install.sh | sh\n ```\n\n <p>It installs the latest prebuilt release into <code>~/.local/bin</code> with a provenance receipt. This is the only channel that <code>xcsh self-update</code> replaces in process. Need a managed or platform-specific channel? Compare the <a href=\"#installation-options\">installation options</a>.</p>\n</section>\n\n## What xcsh helps you do\n\n<div class=\"landing-grid landing-capabilities\">\n <Card title=\"Agentic terminal work\" icon=\"terminal\">\n Run interactive or print-mode prompts, inspect files, use tools, and keep durable session history when the task needs it. [Learn the agent loop](/xcsh/en/use-xcsh/agent-loop/).\n </Card>\n <Card title=\"Reviewable F5 Distributed Cloud workflows\" icon=\"document\">\n Discover schemas, validate manifests, and inspect proposed differences before an authorized resource change. [Use resource workflows](/xcsh/en/f5-distributed-cloud/).\n </Card>\n <Card title=\"Extensible automation\" icon=\"puzzle\">\n Add headless workflows, custom tools, skills, or plugins when built-in commands are not enough. [Automate and extend](/xcsh/en/automate-extend/).\n </Card>\n</div>\nThese workflows start with a working executable; choose another channel only when the universal installer is not the right fit.\n\n## Installation options\n\nUse one channel for install and upgrades. Mixing package-manager and standalone installations can leave more than one `xcsh` on `PATH`; the universal installer above is not repeated here.\n\n<Tabs syncKey=\"install-platform\">\n<TabItem label=\"macOS\" icon=\"seti:apple\">\nInstall the signed and notarized macOS archive through the Homebrew cask. The cask preserves the released executable and native addon byte for byte and does not require `sudo`:\n\n```bash\nbrew install --cask f5-sales-demo/tap/xcsh\n```\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nUpgrade with `brew upgrade --cask f5-sales-demo/tap/xcsh`. Remove the cask with `brew uninstall --cask xcsh`. `xcsh self-update` reports the tap-qualified command but never invokes Homebrew. These commands preserve configuration, credentials, and sessions under `~/.xcsh`.\n</details>\n\n<Aside type=\"caution\" title=\"Clean break from the historical package cask\">\nBefore installing this cask on a Mac that received an earlier xcsh package,\nuninstall both old Homebrew entries (`brew uninstall --formula xcsh` and\n`brew uninstall --cask xcsh`). Then have MDM forget the `com.f5.xcsh` receipt\nand remove only the package-owned `/usr/local/bin/xcsh` and\n`/Library/Application Support/xcsh` payload. Do not remove `~/.xcsh`. The signed\nand stapled `.pkg` release assets remain available only for direct MDM\ndeployment.\n</Aside>\n</TabItem>\n\n<TabItem label=\"Ubuntu or Debian\" icon=\"seti:ubuntu\">\nThe signed apt repository supports amd64 and arm64 and follows normal system upgrades:\n\n```bash\nsudo install -d -m 0755 /etc/apt/keyrings\ncurl -fsSL https://f5-sales-demo.github.io/apt-repo/f5xc-archive-keyring.gpg \\\n | sudo tee /etc/apt/keyrings/f5xc-archive-keyring.gpg >/dev/null\necho \"deb [arch=amd64,arm64 signed-by=/etc/apt/keyrings/f5xc-archive-keyring.gpg] https://f5-sales-demo.github.io/apt-repo stable main\" \\\n | sudo tee /etc/apt/sources.list.d/f5xc.list\nsudo apt-get update && sudo apt-get install xcsh\n```\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nUpgrade with `sudo apt-get update && sudo apt-get install --only-upgrade xcsh`. Remove the package with `sudo apt-get remove xcsh`. `xcsh self-update` reports this command when `dpkg-query` proves that package `xcsh` owns the running executable; it never invokes apt.\n</details>\n</TabItem>\n\n<TabItem label=\"Windows\" icon=\"seti:windows\">\nRun the PowerShell installer, then restart the terminal so its user `PATH` refreshes:\n\n```powershell\nirm https://raw.githubusercontent.com/f5-sales-demo/xcsh/main/scripts/install.ps1 | iex\n```\n\nIt installs `xcsh.exe` and native add-ons to `%LOCALAPPDATA%\\\\xcsh`. **Bash is required for tool-backed sessions.** Install Git for Windows, WSL, Cygwin, or MSYS2; set `shellPath` in `%USERPROFILE%\\\\.xcsh\\\\agent\\\\settings.json` when Bash is elsewhere.\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nRe-run the installer to upgrade. `xcsh self-update` reports the PowerShell installer command but does not replace a Windows installer-owned executable. To uninstall, remove `%LOCALAPPDATA%\\\\xcsh` and its entry from the user `PATH`.\n</details>\n</TabItem>\n\n<TabItem label=\"Container\" icon=\"seti:docker\">\nDocker and Podman use the same multi-architecture image. Pin a release tag, resolve its digest for deployment, and mount only the workspace and state required by the task:\n\n```bash\ndocker pull ghcr.io/f5-sales-demo/xcsh:vX.Y.Z\ndocker image inspect --format '{{index .RepoDigests 0}}' ghcr.io/f5-sales-demo/xcsh:vX.Y.Z\ndocker run --rm ghcr.io/f5-sales-demo/xcsh@sha256:<digest> --version\n```\n\nPodman accepts the same pull, inspect, and run arguments. Pass provider credentials at runtime through a secret manager or environment file; never bake them into an image layer.\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nPull a newer pinned tag to upgrade. Remove an unused image with `docker image rm IMAGE` or `podman image rm IMAGE`.\n</details>\n</TabItem>\n\n<TabItem label=\"Packages and source\" icon=\"seti:config\">\nUse a JavaScript package or a source/ref install when you need a toolchain or an unreleased ref. Source installs require Git and Bun 1.4.2 or newer:\n\n```bash\nbun install --global @f5-sales-demo/xcsh\nnpm install --global @f5-sales-demo/xcsh\ncurl -fsSL https://raw.githubusercontent.com/f5-sales-demo/xcsh/main/scripts/install.sh \\\n | sh -s -- --source --ref vX.Y.Z\n```\n\nThe explicit `--source` and `--ref` arguments select a reproducible source install.\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nUpgrade with `bun update --global @f5-sales-demo/xcsh` or\n`npm update --global @f5-sales-demo/xcsh`. `xcsh self-update` reports the matching\ncommand for a validated compiled-release launcher but never invokes the package\nmanager. Remove the package with `bun remove --global @f5-sales-demo/xcsh` or\n`npm uninstall --global @f5-sales-demo/xcsh`; re-run the source command at a new\nref to replace that unsupported development channel.\n</details>\n</TabItem>\n</Tabs>\n\nRun `xcsh self-update --check` for a read-only version and installation-channel report. A direct MDM package remains organization-managed. Missing, stale, spoofed, or conflicting provenance is blocked without downloading, invoking a package manager, or changing the executable; reinstall through the documented channel instead of mixing installers.\n\nWhichever installation channel you choose, open xcsh next.\n\n<span id=\"verification\" aria-hidden=\"true\"></span>\n\n## Verify installation\n\n<span id=\"verify-the-active-installation\" aria-hidden=\"true\"></span>\n\nStart the interactive terminal UI:\n\n<div class=\"landing-command\">\n\n```bash\nxcsh\n```\n\n</div>\n\nWhen the xcsh prompt appears, connect a model provider from inside the TUI.\n\n<span id=\"authentication\" aria-hidden=\"true\"></span>\n\n## Authenticate a provider\n\nAt the xcsh prompt, open the provider picker:\n\n<div class=\"landing-command\">\n\n```text\n/login\n```\n\n</div>\n\nChoose a provider and follow its sign-in flow. You can also enter `/login PROVIDER` to open a known provider directly. Keep credentials in the provider-supported environment or subscription login—never in a prompt or committed settings file. See [model authentication](/xcsh/en/getting-started/model-authentication/) for provider-specific options.\n\nGPT-6 Luna and Sol use a 272K-token context window by default for ChatGPT subscription sessions.\nTo opt in to their published 1.05M-token window, open `/settings`, select **Providers**, and enable\n**OpenAI Codex Maximum Context**. OpenAI applies higher token pricing to requests above 272K.\nInternal LiteLLM GPT routes use the same 272K default independently; enable **LiteLLM Maximum\nContext** to opt those routes into their verified 1.05M-token window.\n\nAfter sign-in selects a model, stay in the TUI and send a small first prompt.\n\n## Quickstart\n\nAt the xcsh prompt, enter:\n\n<div class=\"landing-command\">\n\n```text\nReply with only: xcsh is ready\n```\n\n</div>\n\nA matching response confirms the installation, provider, and selected model are working together\nwithout granting a tool any file or network side effect. If it fails, verify the active executable\nwith `command -v xcsh`, reopen `/login`, and confirm the selected model is available to that\naccount. Next, learn the [interactive TUI](/xcsh/en/use-xcsh/interactive-tui/) or choose a [context and\nnamespace](/xcsh/en/f5-distributed-cloud/contexts-namespaces/) before an authorized F5 Distributed Cloud\nchange.\n\nFrom here, continue with the documentation path that matches your goal.\n\n<span id=\"exploration\" aria-hidden=\"true\"></span>\n\n## Explore by goal\n\nChoose the goal closest to your next task; each path starts from the verified installation above.\n\n<div class=\"landing-grid landing-explore\">\n <LinkCard title=\"Use xcsh\" icon=\"terminal\" href=\"./use-xcsh/\">Daily terminal use, tools, sessions, plans, and agents.</LinkCard>\n <LinkCard title=\"F5 Distributed Cloud\" icon=\"cloud\" href=\"./f5-distributed-cloud/\">Contexts, manifests, validation, and reviewable resource changes.</LinkCard>\n <LinkCard title=\"Configure and secure\" icon=\"setting\" href=\"./configure-secure/\">Providers, settings, secrets, and sandbox boundaries.</LinkCard>\n <LinkCard title=\"Automate and extend\" icon=\"puzzle\" href=\"./automate-extend/\">Headless modes, extensions, skills, plugins, and SDKs.</LinkCard>\n <LinkCard title=\"Integrations and deployment\" icon=\"rocket\" href=\"./integrations-deployment/\">Containers plus VS Code, Chrome, Office, and Herdr.</LinkCard>\n <LinkCard title=\"Reference and troubleshooting\" icon=\"information\" href=\"./reference-troubleshooting/\">Diagnostics, command reference, hotkeys, and configuration schema.</LinkCard>\n</div>\n",
43
+ "en/index.mdx": "---\ntitle: xcsh\ndescription: Install xcsh, authenticate a model provider, and begin a reviewable terminal or F5 Distributed Cloud workflow.\ntemplate: splash\neditUrl: false\nsidebar:\n hidden: true\n order: 0\nhead:\n - tag: style\n content: |\n main:has(.landing-hero) > .content-panel:first-of-type { padding-block-end: .5rem; }\n main:has(.landing-hero) > .content-panel:first-of-type + .content-panel { border-top: 0; padding-block-start: 0; }\n .landing-hero { display: grid; gap: clamp(2rem, 4vw, 4rem); grid-template-columns: minmax(0, 1fr) minmax(18rem, 35rem); align-items: center; margin-block: .75rem 2rem; }\n .landing-hero__content, .landing-hero__image, .landing-installer, .landing-installer .expressive-code, .landing-command { min-width: 0; }\n h1#_top { max-width: 13ch; margin-block-end: 0; text-wrap: balance; }\n .landing-hero__lede { max-width: 42rem; color: var(--sl-color-gray-2); font-size: var(--sl-text-lg); }\n .landing-installer { margin-block: 0 4rem; padding: clamp(1rem, 2vw, 1.5rem); border: 1px solid var(--sl-color-gray-5); border-radius: .75rem; background: var(--sl-color-bg-nav); }\n .landing-installer h2 { margin: 0; font-size: var(--sl-text-lg); }\n .landing-installer p { margin-block: .5rem 0; }\n .landing-installer pre { max-width: 100%; overflow-x: auto; }\n .landing-installer .expressive-code .ec-line .code { padding-inline-end: 3.5rem; white-space: pre; overflow-wrap: normal; }\n .landing-installer .expressive-code .copy button { opacity: 1; }\n .landing-hero__image { margin: 0; }\n .landing-hero__image img { display: block; width: 100%; height: auto; aspect-ratio: 1442 / 1268; object-fit: contain; filter: drop-shadow(0 1rem 2.5rem color-mix(in srgb, var(--sl-color-accent) 28%, transparent)); }\n .landing-grid { display: grid; gap: 1rem; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-block: 1.5rem; }\n .landing-explore { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n details.landing-disclosure { margin-block: 1rem 0; padding: .75rem 1rem; border: 1px solid var(--sl-color-gray-5); border-radius: .5rem; background: var(--sl-color-bg-nav); }\n details.landing-disclosure summary { cursor: pointer; font-weight: 600; }\n :is(.landing-hero, .landing-installer, .landing-grid, .landing-disclosure) :is(a, button, summary):focus-visible { outline: 2px solid var(--sl-color-accent-high); outline-offset: 3px; }\n @media (max-width: 50rem) { .landing-hero { grid-template-columns: minmax(0, 1fr); gap: 2rem; margin-block: .5rem 2rem; } .landing-installer { margin-block-end: 3rem; padding: 1rem; } .landing-grid { grid-template-columns: minmax(0, 1fr); } }\n---\n\nimport { Aside, Card, LinkCard, TabItem, Tabs } from '@astrojs/starlight/components';\n\n<section class=\"landing-hero\" aria-labelledby=\"_top\">\n <div class=\"landing-hero__content\">\n <p class=\"landing-hero__lede\">An AI-native terminal assistant for engineering, automation, and reviewable F5 Distributed Cloud workflows.</p>\n <p>xcsh is an independent open-source project derived from pi/pi-mono, specialized for F5 Distributed Cloud workflows; it is not an F5 product.</p>\n </div>\n\n <figure class=\"landing-hero__image\">\n <img src=\"/xcsh/images/xcsh-ghostty-terminal.png\" width=\"1442\" height=\"1268\" alt=\"xcsh running in a Ghostty terminal with the model selector, prompt, and session status visible.\" />\n </figure>\n</section>\n\n<section class=\"landing-installer\" aria-labelledby=\"install-xcsh\">\n\n## Install xcsh\n\n <p>Universal installer for macOS and Linux.</p>\n\n ```bash\n curl -fsSL https://raw.githubusercontent.com/f5-sales-demo/xcsh/main/scripts/install.sh | sh\n ```\n\n <p>It installs the latest prebuilt release and a schema-v2 provenance receipt into <code>~/.local/bin</code>. The native addon is embedded in the executable, so those are the only two standalone payloads. This is the only channel that <code>xcsh self-update</code> replaces in process. Need a managed or platform-specific channel? Compare the <a href=\"#installation-options\">installation options</a>.</p>\n</section>\n\n## What xcsh helps you do\n\n<div class=\"landing-grid landing-capabilities\">\n <Card title=\"Agentic terminal work\" icon=\"terminal\">\n Run interactive or print-mode prompts, inspect files, use tools, and keep durable session history when the task needs it. [Learn the agent loop](/xcsh/en/use-xcsh/agent-loop/).\n </Card>\n <Card title=\"Reviewable F5 Distributed Cloud workflows\" icon=\"document\">\n Discover schemas, validate manifests, and inspect proposed differences before an authorized resource change. [Use resource workflows](/xcsh/en/f5-distributed-cloud/).\n </Card>\n <Card title=\"Extensible automation\" icon=\"puzzle\">\n Add headless workflows, custom tools, skills, or plugins when built-in commands are not enough. [Automate and extend](/xcsh/en/automate-extend/).\n </Card>\n</div>\nThese workflows start with a working executable; choose another channel only when the universal installer is not the right fit.\n\n## Installation options\n\nUse one channel for install and upgrades. Mixing package-manager and standalone installations can leave more than one `xcsh` on `PATH`; the universal installer above is not repeated here.\n\n<Tabs syncKey=\"install-platform\">\n<TabItem label=\"macOS\" icon=\"seti:apple\">\nInstall the signed and notarized macOS archive through the Homebrew cask. The cask preserves the released executable and native addon byte for byte and does not require `sudo`:\n\n```bash\nbrew install --cask f5-sales-demo/tap/xcsh\n```\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nUpgrade with `brew upgrade --cask f5-sales-demo/tap/xcsh`. Remove the cask with `brew uninstall --cask xcsh`. `xcsh self-update` reports the tap-qualified command but never invokes Homebrew. These commands preserve configuration, credentials, and sessions under `~/.xcsh`.\n</details>\n\n<Aside type=\"caution\" title=\"Clean break from the historical package cask\">\nBefore installing this cask on a Mac that received an earlier xcsh package,\nuninstall both old Homebrew entries (`brew uninstall --formula xcsh` and\n`brew uninstall --cask xcsh`). Then have MDM forget the `com.f5.xcsh` receipt\nand remove only the package-owned `/usr/local/bin/xcsh` and\n`/Library/Application Support/xcsh` payload. Do not remove `~/.xcsh`. The signed\nand stapled `.pkg` release assets remain available only for direct MDM\ndeployment.\n</Aside>\n</TabItem>\n\n<TabItem label=\"Ubuntu or Debian\" icon=\"seti:ubuntu\">\nThe signed apt repository supports amd64 and arm64 and follows normal system upgrades:\n\n```bash\nsudo install -d -m 0755 /etc/apt/keyrings\ncurl -fsSL https://f5-sales-demo.github.io/apt-repo/f5xc-archive-keyring.gpg \\\n | sudo tee /etc/apt/keyrings/f5xc-archive-keyring.gpg >/dev/null\necho \"deb [arch=amd64,arm64 signed-by=/etc/apt/keyrings/f5xc-archive-keyring.gpg] https://f5-sales-demo.github.io/apt-repo stable main\" \\\n | sudo tee /etc/apt/sources.list.d/f5xc.list\nsudo apt-get update && sudo apt-get install xcsh\n```\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nUpgrade with `sudo apt-get update && sudo apt-get install --only-upgrade xcsh`. Remove the package with `sudo apt-get remove xcsh`. `xcsh self-update` reports this command when `dpkg-query` proves that package `xcsh` owns the running executable; it never invokes apt.\n</details>\n</TabItem>\n\n<TabItem label=\"Windows\" icon=\"seti:windows\">\nRun the PowerShell installer, then restart the terminal so its user `PATH` refreshes:\n\n```powershell\nirm https://raw.githubusercontent.com/f5-sales-demo/xcsh/main/scripts/install.ps1 | iex\n```\n\nIt installs `xcsh.exe` and its schema-v2 provenance receipt to `%LOCALAPPDATA%\\\\xcsh`; the native addon is embedded. **Bash is required for tool-backed sessions.** Install Git for Windows, WSL, Cygwin, or MSYS2; set `shellPath` in `%USERPROFILE%\\\\.xcsh\\\\agent\\\\settings.json` when Bash is elsewhere.\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nRe-run the installer to upgrade. `xcsh self-update` reports the PowerShell installer command but does not replace a Windows installer-owned executable. To uninstall, remove `%LOCALAPPDATA%\\\\xcsh` and its entry from the user `PATH`.\n</details>\n</TabItem>\n\n<TabItem label=\"Container\" icon=\"seti:docker\">\nDocker and Podman use the same multi-architecture image. Pin a release tag, resolve its digest for deployment, and mount only the workspace and state required by the task:\n\n```bash\ndocker pull ghcr.io/f5-sales-demo/xcsh:vX.Y.Z\ndocker image inspect --format '{{index .RepoDigests 0}}' ghcr.io/f5-sales-demo/xcsh:vX.Y.Z\ndocker run --rm ghcr.io/f5-sales-demo/xcsh@sha256:<digest> --version\n```\n\nPodman accepts the same pull, inspect, and run arguments. Pass provider credentials at runtime through a secret manager or environment file; never bake them into an image layer.\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nPull a newer pinned tag to upgrade. Remove an unused image with `docker image rm IMAGE` or `podman image rm IMAGE`.\n</details>\n</TabItem>\n\n<TabItem label=\"Packages and source\" icon=\"seti:config\">\nUse a JavaScript package or a source/ref install when you need a toolchain or an unreleased ref. Source installs require Git and Bun 1.4.2 or newer:\n\n```bash\nbun install --global @f5-sales-demo/xcsh\nnpm install --global @f5-sales-demo/xcsh\ncurl -fsSL https://raw.githubusercontent.com/f5-sales-demo/xcsh/main/scripts/install.sh \\\n | sh -s -- --source --ref vX.Y.Z\n```\n\nThe explicit `--source` and `--ref` arguments select a reproducible source install.\n\n<details class=\"landing-disclosure\">\n<summary>Upgrade or remove</summary>\n\nUpgrade with `bun update --global @f5-sales-demo/xcsh` or\n`npm update --global @f5-sales-demo/xcsh`. `xcsh self-update` reports the matching\ncommand for a validated compiled-release launcher but never invokes the package\nmanager. Remove the package with `bun remove --global @f5-sales-demo/xcsh` or\n`npm uninstall --global @f5-sales-demo/xcsh`; re-run the source command at a new\nref to replace that unsupported development channel.\n</details>\n</TabItem>\n</Tabs>\n\nRun `xcsh self-update --check` for a read-only version and installation-channel report. A direct MDM package remains organization-managed. Missing, stale, spoofed, or conflicting provenance is blocked without downloading, invoking a package manager, or changing the executable; reinstall through the documented channel instead of mixing installers.\n\n<Aside type=\"caution\" title=\"Standalone receipt schema v2\">\nStandalone installations with an older schema-v1 receipt must run the documented installer once before using `xcsh self-update`. There is no compatibility parser or automatic receipt migration. A successful self-update advances the executable and adjacent receipt together and restores both if the transaction fails.\n</Aside>\n\nWhichever installation channel you choose, open xcsh next.\n\n<span id=\"verification\" aria-hidden=\"true\"></span>\n\n## Verify installation\n\n<span id=\"verify-the-active-installation\" aria-hidden=\"true\"></span>\n\nStart the interactive terminal UI:\n\n<div class=\"landing-command\">\n\n```bash\nxcsh\n```\n\n</div>\n\nWhen the xcsh prompt appears, connect a model provider from inside the TUI.\n\n<span id=\"authentication\" aria-hidden=\"true\"></span>\n\n## Authenticate a provider\n\nAt the xcsh prompt, open the provider picker:\n\n<div class=\"landing-command\">\n\n```text\n/login\n```\n\n</div>\n\nChoose a provider and follow its sign-in flow. You can also enter `/login PROVIDER` to open a known provider directly. Keep credentials in the provider-supported environment or subscription login—never in a prompt or committed settings file. See [model authentication](/xcsh/en/getting-started/model-authentication/) for provider-specific options.\n\nGPT-6 Luna and Sol use a 272K-token context window by default for ChatGPT subscription sessions.\nTo opt in to their published 1.05M-token window, open `/settings`, select **Providers**, and enable\n**OpenAI Codex Maximum Context**. OpenAI applies higher token pricing to requests above 272K.\nInternal LiteLLM GPT routes use the same 272K default independently; enable **LiteLLM Maximum\nContext** to opt those routes into their verified 1.05M-token window.\n\nAfter sign-in selects a model, stay in the TUI and send a small first prompt.\n\n## Quickstart\n\nAt the xcsh prompt, enter:\n\n<div class=\"landing-command\">\n\n```text\nReply with only: xcsh is ready\n```\n\n</div>\n\nA matching response confirms the installation, provider, and selected model are working together\nwithout granting a tool any file or network side effect. If it fails, verify the active executable\nwith `command -v xcsh`, reopen `/login`, and confirm the selected model is available to that\naccount. Next, learn the [interactive TUI](/xcsh/en/use-xcsh/interactive-tui/) or choose a [context and\nnamespace](/xcsh/en/f5-distributed-cloud/contexts-namespaces/) before an authorized F5 Distributed Cloud\nchange.\n\nFrom here, continue with the documentation path that matches your goal.\n\n<span id=\"exploration\" aria-hidden=\"true\"></span>\n\n## Explore by goal\n\nChoose the goal closest to your next task; each path starts from the verified installation above.\n\n<div class=\"landing-grid landing-explore\">\n <LinkCard title=\"Use xcsh\" icon=\"terminal\" href=\"./use-xcsh/\">Daily terminal use, tools, sessions, plans, and agents.</LinkCard>\n <LinkCard title=\"F5 Distributed Cloud\" icon=\"cloud\" href=\"./f5-distributed-cloud/\">Contexts, manifests, validation, and reviewable resource changes.</LinkCard>\n <LinkCard title=\"Configure and secure\" icon=\"setting\" href=\"./configure-secure/\">Providers, settings, secrets, and sandbox boundaries.</LinkCard>\n <LinkCard title=\"Automate and extend\" icon=\"puzzle\" href=\"./automate-extend/\">Headless modes, extensions, skills, plugins, and SDKs.</LinkCard>\n <LinkCard title=\"Integrations and deployment\" icon=\"rocket\" href=\"./integrations-deployment/\">Containers plus VS Code, Chrome, Office, and Herdr.</LinkCard>\n <LinkCard title=\"Reference and troubleshooting\" icon=\"information\" href=\"./reference-troubleshooting/\">Diagnostics, command reference, hotkeys, and configuration schema.</LinkCard>\n</div>\n",
44
44
  "en/integrations-deployment/chrome.mdx": "---\ntitle: \"Chrome\"\ndescription: \"Use the xcsh Chrome extension and native host with an isolated automation profile.\"\nsidebar:\n order: 3\n label: \"Chrome\"\n---\n\nThe Chrome integration connects xcsh to a browser session for supported console automation. The installed `xcsh chrome --help` output is the command authority.\n\n## What is the safe operating boundary?\n\nUse a dedicated profile and an authorized target. Confirm the visible account, uniform resource locator (URL), and object before an action that changes state. Prefer deterministic F5 Distributed Cloud resource commands whenever they cover the same operation.\n\n## Which Chrome capabilities does xcsh expose?\n\nThe `chrome` command inspects and arranges browser sessions, and `chrome-host` runs the native messaging host that connects the extension to the executable. Page-level interaction is driven by the browser tool inside a session rather than by a subcommand, so scope it with `--tools` in the same way as any other tool.\n\n| Need | Use Chrome integration? | Boundary |\n| --- | --- | --- |\n| Inspect a visible page or perform a supported console-only task | Yes | The browser profile and signed-in session define authority. |\n| Manage a resource with an xcsh catalog command | No | Prefer the typed resource workflow and its diff. |\n| Automate an unsupported site or a third-party account | No | The integration does not expand authorization. |\n\nStart with a read-only inspection and confirm the selected tab, URL, tenant, and browser profile. A click or submitted form can create an external side effect; the transcript must identify that boundary. Close temporary tabs and verify the intended page state after the task.\n",
45
45
  "en/integrations-deployment/containers.mdx": "---\ntitle: \"Containers\"\ndescription: \"Run the published multi-architecture Alpine image with explicit mounts and credentials.\"\nsidebar:\n order: 4\n label: \"Containers\"\nhead:\n - tag: style\n content: |\n .sl-markdown-content .expressive-code { max-width: 100%; overflow-x: auto; }\n .sl-markdown-content table { display: block; max-width: 100%; overflow-x: auto; }\n---\n\nThe published container packages xcsh for process isolation and repeatable deployment. It does not grant cloud credentials or filesystem access by itself.\n\n## How do I verify an image?\n\n```bash\ndocker pull ghcr.io/f5-sales-demo/xcsh:latest\ndocker run --rm ghcr.io/f5-sales-demo/xcsh:latest --version\n```\n\nPin a version tag or digest for automation. Mount only the task directory and inject credentials through the runtime's secret mechanism.\n\n## What must remain outside the image?\n\nKeep user contexts, provider tokens, and writable session data in explicit mounts or secret stores. Do not bake them into a derived layer.\n\nPull a versioned image, resolve its immutable digest, and run `xcsh --version` from that digest\nbefore mounting a workspace. The container image supplies the executable, not model or tenant\nauthority. Mount only required paths read-only where possible and inject credentials at runtime\nthrough the platform's secret mechanism. Remove the test container after exit; delete the pinned\nimage only when no deployment still references its digest.\n",
46
46
  "en/integrations-deployment/herdr.mdx": "---\ntitle: \"Integrate with Herdr\"\ndescription: \"Understand Herdr workspace launch, terminal actions, protocol negotiation, and isolation.\"\nsidebar:\n order: 5\n label: \"Herdr\"\nhead:\n - tag: style\n content: |\n .sl-markdown-content .expressive-code { max-width: 100%; overflow-x: auto; }\n .sl-markdown-content table { display: block; max-width: 100%; overflow-x: auto; }\n---\n\nHerdr support is optional and host-dependent. When the host does not provide it, xcsh terminal operation is unaffected.\n\n## How is the host protocol negotiated?\n\nThe integration checks the available socket protocol and media or graphics capabilities before it requests a workspace. Missing negotiation support leaves ordinary xcsh terminal operation intact.\n\n## How is a conversation-owned workspace launched?\n\nA workspace is tied to the conversation identity supplied at startup. Terminal-management actions create or address panes within that workspace, and lifecycle reporting stays anchored to the same startup context. Do not reuse an unrelated workspace merely because its socket is reachable.\n\n## How do I recover from socket failure?\n\nConnection and framing failures surface as integration diagnostics. The client must not open an inbound listener as a fallback. Close conversation-owned resources through the supported lifecycle and preserve unrelated Herdr workspaces.\n\n## What lifecycle state does xcsh publish?\n\nxcsh publishes native turn phases rather than inferring completion from terminal idleness or process\nexit. With a supported Herdr protocol through 26 and the advertised `agent_turn_journal` capability, a tracked\nexecution publishes a persisted session/turn identity and a bounded semantic result to Herdr's\ndurable journal. The existing 80-character metadata preview remains display-only and is never\ncompletion evidence.\nCommentary, prompt content, tool data, and failed or cancelled partial output are excluded.\n\n### External execution driver contract\n\nThe supported producer is the normal `xcsh` executable launched by Herdr's execution backend; it is not a mock or a no-provider/offline actor. For a journal-tracked invocation, that backend must inject the exact allocated `HERDR_EXECUTION_ID` and decimal safe-integer `HERDR_EXECUTION_GENERATION` into the child environment before it starts `xcsh`.\n\nxcsh never guesses a generation: without both values it retains ordinary pane lifecycle reporting but emits no semantic journal event. A continuation must be launched with its newly allocated generation.\n\nProtocol 27 is the current producer-native contract. Herdr injects a one-time `HERDR_NATIVE_CAPABILITY` into the child. xcsh includes it only in authenticated turn-report and action requests; it is never written to the session journal, logs, diagnostics, or receipts.\nHerdr does not authorize actions until the first `starting` report has been durably journaled and bound to the owned PTY, pane, session, generation, and turn.\n\nEach semantic report has a five-second deadline. xcsh retries exactly once only when timeout,\nEOF, or a transport error leaves admission ambiguous. The retry preserves the method and complete\nsemantic payload and changes only the transport request ID. Either correlated receipt marks the\nevent delivered; protocol, validation, malformed-response, and explicit remote errors are definitive\nand are not retried. Degraded tracking is shown only after both ambiguous attempts fail.\n\nThe producer wire shapes align with [Herdr PR #49](https://github.com/f5-sales-demo/herdr/pull/49) at cancellation-settlement commit `2c838cf545ed06439b4988cedb3e044f1d436264`, starting-replay recovery commit `edd5611cfd7685c0f069f244ceee0ac6c0a0678e`, and native-argv alignment commit `3397302960593a552fb37ceb71053cf99f2619a3`.\nThose commits are an integration contract under independent review, not released-backend evidence.\n\nAfter that registration, xcsh polls `agent.turn.action.get` with the same immutable provenance. A `cancel` action dismisses an active managed UI prompt and calls the real `AgentSession` abort path. xcsh persists the requested action and local safe point before sending the exact `agent.turn.action.ack`; it emits `cancelled` only after Herdr confirms that safe point.\nA lost ACK response is retried with the same action ID and revision, including after reporter restart. A backend `timed_out` action never triggers a late abort. PTY exit alone is not semantic cancellation.\n\nSession shutdown releases pane authority. It does not convert an unfinished turn into a successful completion.\n\n### Producer-native acceptance driver\n\n`xcsh lifecycle --json` prints the version 3 machine-readable producer contract. To execute a source or installed\nproducer check, supply a fresh absolute session directory, a normally configured model, and one scenario:\n\n```bash\nxcsh lifecycle --json \\\n --scenario await-continue \\\n --model openai-codex/gpt-5.6-luna \\\n --session-dir /absolute/fresh/xcsh-lifecycle-session\n```\n\nSupported scenarios are `success`, `failure`, `await-continue`, `cancel`, `managed-cancel`, `managed-working-cancel`, `local-operation-failure`, and `reply-loss-replay`. The driver first\nuses the prompt-free JSON session contract to persist one canonical 16-character lowercase hexadecimal session\nheader, then reopens that exact path in an ordinary interactive xcsh child. It generates a random mode-0600 fixture\nfor successful reads. `await-continue` selects the explicit `await_user_v1` producer control and pauses in the real\n`ExtensionUIController` input surface before the configured\nmodel runs; `cancel` sends SIGINT through `PtySession.interrupt()` and requires xcsh's native `cancelled` transition.\n`managed-cancel` uses a local protocol-27 control fixture to request the action and requires the executable child to\nacknowledge its safe point before its native `cancelled` report. That fixture validates the producer adapter only; it\nis not evidence for Herdr's durable action journal, manager generation replacement, or consumer deduplication.\n`managed-working-cancel` requests the same cooperative action after the real child reports `working`, without opening\nthe acceptance prompt, and verifies cancellation through the active `AgentSession` path.\n`local-operation-failure` selects `local_operation_failure_v1` in a noninteractive native child. The producer creates\na private directory under the child cwd, performs a real read of a guaranteed-missing path inside it, cleans up the\nowned directory, and propagates the resulting ENOENT through normal `AgentSession` failed settlement. It does not use\nan invalid credential, an offline model, or a fabricated lifecycle frame.\n\nThe producer accepts these exact explicit argv mappings; no control is active when the flag is omitted. The published\n`await-user` spelling remains an alias for `await_user_v1` for existing acceptance callers.\n\n| Typed lifecycle mode | Producer argv | Constraint |\n| --- | --- | --- |\n| `managed_turn_v1` | no additional argv | Existing protocol-27 v3 behavior. |\n| `await_user_v1` | `--native-lifecycle-control await_user_v1` | `interactive: true`. |\n| `local_operation_failure_v1` | `--native-lifecycle-control local_operation_failure_v1` | `interactive: false`. |\n\nThe current Herdr protocol-27 schema exposes only `managed_turn_v1`; the other two rows define the producer side of a\nfuture typed schema extension, not permission for a caller to supply arbitrary argv. xcsh does not invent action\nmethods for response loss or restart. Real Herdr-bound retry and restart controls must use a committed authenticated\nbackend schema and preserve the same session, generation, pane, turn, report revision, and request-only capability.\n\nThe reply-loss scenario owns a temporary local protocol-27 socket that acknowledges ordinary reporter traffic but\ndrops both bounded `agent.turn.report` attempts. xcsh creates and persists the semantic event before transport, runs the\nconfigured provider and `read` tool, then a second xcsh process resumes the exact session path. The scenario passes\nonly when the reporter redelivers the byte-equivalent execution, pane, session, turn, generation, event revision, and\nstate fields. The socket never authors a lifecycle frame or substitutes an offline model.\n\nThe receipt's `evidenceClass` is `source_native_child`. This proves the measured source or executable behavior only;\nthe Control Manager remains responsible for its own admission, authenticated execution binding, generation changes,\nconsumer receipts, and installed-artifact acceptance. The command does not install or release xcsh or Herdr and does\nnot change normal launch defaults.\n\nThe verified contract is:\n\n| Concern | xcsh signal | Herdr disposition |\n| --- | --- | --- |\n| Session start and identity | Persisted native session ID/path | Herdr resume reference plus stable semantic session identity. |\n| Turn start/progress | Persisted UUID and normalized `turn_phase` | `starting`/`working` journal events with producer revision and server revision. |\n| Waiting for input/approval | `awaiting_user` and closed prompt kind | `waiting_input` with fixed safe reason; private prompt content is excluded. |\n| Successful completion | Settlement-aware `idle` after `agent_end` | `completed` with at most 8,000 UTF-8 bytes and a matching SHA-256 digest. |\n| Failure/cancellation | `error` or `cancelled` | Distinct terminal state and bounded safe reason; no partial result. |\n| Managed cancellation | Persisted action receipt, Extension UI signal, and `AgentSession.abort()` | Authenticated `safe_point` precedes and is linked to the native `cancelled` report. |\n| Resume/fork | Session events and persisted custom turn entries | Resume preserves identity; unfinished recovered work becomes `interrupted`; fork has separate session lineage. |\n| Reconnect/order | Serialized queue and persisted event revision | Exact duplicates are idempotent; stale/conflicting revisions fail; terminal state is immutable. |\n| Application exit | `session_shutdown` | Active turn becomes `interrupted`, then pane authority is released; never success. |\n\nThe protocol-26 native-interaction update is tracked by\n[issue #4161](https://github.com/f5-sales-demo/xcsh/issues/4161); its producer lifecycle foundation is\ntracked by [issue #3791](https://github.com/f5-sales-demo/xcsh/issues/3791) and\n[issue #3768](https://github.com/f5-sales-demo/xcsh/issues/3768). The active protocol and lifecycle\nbehavior is described on this page; obsolete delivery checklists are not part of the runtime contract.\n\nHerdr lifecycle reconciliation is tracked by issue #5 in the F5 Herdr fork. Current source already recognizes the exact `(\"herdr:xcsh\", \"xcsh\")` resume pair; deployed build provenance and live resume/fork acceptance remain required before feature acceptance.\n\nTreat the negotiated protocol and the pane lifecycle as separate checks. First confirm that the\nHerdr socket and protocol version are compatible, then launch one conversation-owned workspace and\nobserve its published state through exit. xcsh owns only workspaces it created for that\nconversation; it must not close unrelated panes. After a forced socket failure, reconnect and verify\nthat stale workspace state is reconciled before another launch.\n",
47
47
  "en/integrations-deployment/index.mdx": "---\ntitle: \"Integrations and deployment\"\ndescription: \"Deploy xcsh in containers and connect the editor, browser, Office, and Herdr surfaces.\"\nsidebar:\n order: 0\n label: \"Integrations\"\n---\n\nChoose an integration by host, authority, and lifecycle boundary.\n\n## Which integration fits?\n\n- [Containers](/xcsh/en/integrations-deployment/containers/) for an isolated packaged runtime.\n- [VS Code](/xcsh/en/integrations-deployment/vscode/), [Chrome](/xcsh/en/integrations-deployment/chrome/), [Office](/xcsh/en/integrations-deployment/office/), or [Herdr](/xcsh/en/integrations-deployment/herdr/) only when that companion surface is installed and the task needs it.\n",
48
48
  "en/integrations-deployment/office.mdx": "---\ntitle: \"Microsoft Office task pane\"\ndescription: \"Serve or sideload the embedded task pane for Excel, Word, and PowerPoint.\"\nsidebar:\n order: 2\n label: \"Office task pane\"\n---\n\nThe Office pane packages an xcsh sidebar for supported Excel, Word, and PowerPoint hosts. `xcsh office --help` describes the installed serve, print, and sideload actions.\n\n## Who owns the test surface?\n\nPackage tests own pane components, bridge messages, and host-independent state. Office-host integration tests own document application programming interfaces (APIs), sideloading, and task-pane lifecycle. A change that crosses the bridge requires both suites; a passing browser-style component test does not prove Word, Excel, or PowerPoint host behavior.\n\n## What must be running?\n\nThe pane build and its local bridge server must match the xcsh version used for the session. One active sidebar binds to one server session at a time.\n\n## Which host steps vary by platform?\n\nRibbon placement, the sideload mechanism, and trust prompts differ across Office on Windows, Office on macOS, and Office on the web. Read the sideload action from `xcsh office --help`, then follow the Microsoft sideloading procedure for the specific host you are targeting.\n\n| Goal | Recommended surface | Why |\n| --- | --- | --- |\n| Edit or analyze a standalone document | The document workflow | It can render and verify the saved artifact. |\n| Control the open Office task pane | The Office integration | It operates in the signed-in host application. |\n| Run a repeatable batch job | Headless mode or SDK | It avoids UI-dependent focus and selection. |\n\nThe Office host, active document, selection, and signed-in account determine scope. Confirm all four before a write. Save to a test copy, reopen it, and inspect the changed region; closing a dialog is not proof that the document persisted.\n",
49
49
  "en/integrations-deployment/vscode.mdx": "---\ntitle: \"VS Code\"\ndescription: \"Use the companion VS Code integration with the same xcsh model and session foundation.\"\nsidebar:\n order: 1\n label: \"VS Code\"\n---\n\nThe companion VS Code extension embeds an xcsh conversation in an editor workspace. Its extension manifest and source repository define the supported commands and distribution channel.\n\n## What carries over from the terminal?\n\nProvider configuration, model selection, session concepts, and workspace file boundaries follow the xcsh runtime. Open the intended workspace before starting a conversation.\n\n## Which behavior should I validate in the terminal?\n\nEditor menus and command palette entries belong to the extension host and change with its release. Reproduce runtime behavior with the command-line interface (CLI) first:\n\n1. Run the same prompt with `xcsh -p` in the workspace directory.\n2. Compare the model, provider, and tool set reported there with the editor session.\n3. Inspect the persisted session file rather than the editor transcript when an exact sequence matters.\n\nA difference between the two points at extension configuration, not at the agent loop.\n\n| Need | Use VS Code? | Alternative |\n| --- | --- | --- |\n| Work with editor selection and workspace context | Yes | The terminal cannot infer the active editor selection. |\n| Reproduce provider, tool, or session behavior | Usually no | Reproduce with the xcsh CLI first. |\n| Run unattended automation | No | Use a headless protocol or the SDK. |\n\nThe extension inherits the VS Code workspace trust and process environment but still uses xcsh provider and tool boundaries. Confirm the workspace root and selected file before applying an edit. Re-run the same prompt in the terminal when isolating whether a failure belongs to xcsh or the editor host.\n",
50
- "en/reference-troubleshooting/cli-reference.mdx": "---\ntitle: \"CLI command and flag reference\"\ndescription: \"Look up every top-level xcsh command and every global flag a session accepts.\"\nsidebar:\n order: 1\n label: \"CLI reference\"\n---\n\nThe installed `xcsh <COMMAND> --help` output is the command-line interface (CLI) authority for its release. This page lists the command and flag names that release exposes so you can find one without starting a session.\n\n## Which top-level commands exist?\n\n| Area | Commands |\n| --- | --- |\n| Resources | `validate`, `diff`, `create`, `apply`, `update`, `get`, `export`, `delete` |\n| Runtime | `config`, `setup`, `sandbox`, `shell`, `ssh`, `stats`, `search` |\n| Files and code | `read`, `grep`, `jupyter` |\n| Extensibility | `agents`, `plugin`, `worker`, `manager` |\n| Interfaces | `chrome`, `chrome-host`, `office`, `herdr` |\n| Repository | `commit`, `grievances` |\n| Maintenance | `self-update` |\n\n`search` also answers to `q`. `update -f` names a resource manifest; executable checks and updates use `self-update`.\n\n`xcsh self-update --check` is read-only and reports the installation channel. Homebrew,\napt, npm, Bun, Windows installer, and direct MDM installations retain ownership:\nthe command prints their exact upgrade instruction or managed-install message and\nperforms no update. Only a standalone Unix executable with a valid adjacent\n`xcsh-install.json` receipt can replace itself. Unknown or conflicting provenance\nexits nonzero without changing files, and `--force` does not bypass this policy.\n\n## Which global flags does a session accept?\n\nThese flags configure an agent launch. They precede the prompt and do not apply to a subcommand.\n\n| Group | Flags |\n| --- | --- |\n| Model | `--model`, `--smol`, `--slow`, `--plan`, `--provider`, `--models`, `--thinking`, `--list-models` |\n| Credentials and context | `--api-key`, `--context` |\n| Prompt | `--system-prompt`, `--append-system-prompt`, `--no-title` |\n| Mode | `--mode` (`text`, `json`, `rpc`, `acp`), `-p`/`--print` |\n| Session | `-c`/`--continue`, `-r`/`--resume`, `--fork`, `--session-dir`, `--no-session`, `--no-memories`, `--export` |\n| Filesystem | `--allow-home`, `--allow-path`, `--no-sandbox` |\n| Capabilities | `--tools`, `--no-tools`, `--no-lsp`, `--no-pty` |\n| Discovery | `-e`/`--extension`, `--hook`, `--plugin-dir`, `--no-extensions`, `--skills`, `--no-skills`, `--no-rules` |\n\n`packages/coding-agent/src/cli/flag-spec.ts` is the single source that renders `--help` and drives the parser, so the two cannot disagree.\n\n## How do I script a command safely?\n\nRun these steps against the pinned executable:\n\n1. Read `xcsh <COMMAND> --help` for the release you pinned.\n2. Write long flags rather than single-character aliases.\n3. Request `--result-file` or `--mode json` wherever the command supports it.\n4. Pass `--allow-path` once per additional directory; it accumulates rather than replacing.\n\nTreat `xcsh --help` and each subcommand's `--help` as the version-matched authority. Global flags\nmust precede or follow arguments exactly as that help permits; protocol modes reserve stdout for\nframed data. Before scripting a mutating command, pin the xcsh version, pass the context and\nresource identifiers explicitly, and exercise the read or validation form. Exit status is the\nautomation contract; terminal color and progress text are not.\n",
50
+ "en/reference-troubleshooting/cli-reference.mdx": "---\ntitle: \"CLI command and flag reference\"\ndescription: \"Look up every top-level xcsh command and every global flag a session accepts.\"\nsidebar:\n order: 1\n label: \"CLI reference\"\n---\n\nThe installed `xcsh <COMMAND> --help` output is the command-line interface (CLI) authority for its release. This page lists the command and flag names that release exposes so you can find one without starting a session.\n\n## Which top-level commands exist?\n\n| Area | Commands |\n| --- | --- |\n| Resources | `validate`, `diff`, `create`, `apply`, `update`, `get`, `export`, `delete` |\n| Runtime | `config`, `setup`, `sandbox`, `shell`, `ssh`, `stats`, `search` |\n| Files and code | `read`, `grep`, `jupyter` |\n| Extensibility | `agents`, `plugin`, `worker`, `manager` |\n| Interfaces | `chrome`, `chrome-host`, `office`, `herdr` |\n| Repository | `commit`, `grievances` |\n| Maintenance | `self-update` |\n\n`search` also answers to `q`. `update -f` names a resource manifest; executable checks and updates use `self-update`.\n\n`xcsh self-update --check` is read-only and reports the installation channel. Homebrew,\napt, npm, Bun, Windows installer, and direct MDM installations retain ownership:\nthe command prints their exact upgrade instruction or managed-install message and\nperforms no update. Only a standalone Unix executable with a valid adjacent\n`xcsh-install.json` receipt can replace itself. Unknown or conflicting provenance\nexits nonzero without changing files, and `--force` does not bypass this policy.\nThe standalone receipt uses schema v2 and records the canonical absolute executable\npath. Schema-v1 installations must run the documented installer once; they are not\nparsed or migrated by `self-update`. Executable and receipt replacement is one\nrollback-safe transaction.\n\n## Which global flags does a session accept?\n\nThese flags configure an agent launch. They precede the prompt and do not apply to a subcommand.\n\n| Group | Flags |\n| --- | --- |\n| Model | `--model`, `--smol`, `--slow`, `--plan`, `--provider`, `--models`, `--thinking`, `--list-models` |\n| Credentials and context | `--api-key`, `--context` |\n| Prompt | `--system-prompt`, `--append-system-prompt`, `--no-title` |\n| Mode | `--mode` (`text`, `json`, `rpc`, `acp`), `-p`/`--print` |\n| Session | `-c`/`--continue`, `-r`/`--resume`, `--fork`, `--session-dir`, `--no-session`, `--no-memories`, `--export` |\n| Filesystem | `--allow-home`, `--allow-path`, `--no-sandbox` |\n| Capabilities | `--tools`, `--no-tools`, `--no-lsp`, `--no-pty` |\n| Discovery | `-e`/`--extension`, `--hook`, `--plugin-dir`, `--no-extensions`, `--skills`, `--no-skills`, `--no-rules` |\n\n`packages/coding-agent/src/cli/flag-spec.ts` is the single source that renders `--help` and drives the parser, so the two cannot disagree.\n\n## How do I script a command safely?\n\nRun these steps against the pinned executable:\n\n1. Read `xcsh <COMMAND> --help` for the release you pinned.\n2. Write long flags rather than single-character aliases.\n3. Request `--result-file` or `--mode json` wherever the command supports it.\n4. Pass `--allow-path` once per additional directory; it accumulates rather than replacing.\n\nTreat `xcsh --help` and each subcommand's `--help` as the version-matched authority. Global flags\nmust precede or follow arguments exactly as that help permits; protocol modes reserve stdout for\nframed data. Before scripting a mutating command, pin the xcsh version, pass the context and\nresource identifiers explicitly, and exercise the read or validation form. Exit status is the\nautomation contract; terminal color and progress text are not.\n",
51
51
  "en/reference-troubleshooting/configuration-schema.mdx": "---\ntitle: \"Configuration schema\"\ndescription: \"Look up the settings namespaces xcsh validates, and the commands that report effective values.\"\nsidebar:\n order: 3\n label: \"Configuration schema\"\n---\n\n`packages/coding-agent/src/config/settings-schema.ts` defines every recognized settings key with its type, default, and validation. It evolves with the executable, so read the key namespaces here and the values from your own installation.\n\n## Which key namespaces exist?\n\n| Area | Namespaces |\n| --- | --- |\n| Appearance | `theme`, `statusLine`, `display`, `tui`, `terminal`, `images`, `media` |\n| Model and routing | `routing`, `retry`, `thinkingBudgets`, `providers`, `exa` |\n| Interaction | `startup`, `keybindings`, `completion`, `ask`, `stt`, `marketplace` |\n| Context and session | `context`, `contextPromotion`, `compaction`, `branchSummary`, `memories`, `ttsr`, `power` |\n| Editing | `edit`, `read`, `lsp`, `bashInterceptor`, `python` |\n| Tools | `tools`, `todo`, `find`, `grep`, `astGrep`, `astEdit`, `notebook`, `fetch`, `browser`, `web_search`, `calc`, `checkpoint`, `debug`, `inspect_image`, `generate_image`, `renderMermaid`, `bash`, `async` |\n| Tasks and capabilities | `task`, `tasks`, `skills`, `commands` |\n| Security | `secrets`, `sandbox`, `xcsh` |\n| Repository workflows | `commit`, `dev` |\n\nA key is `namespace.name`, as in `compaction.thresholdPercent` or `sandbox.allowRead`. Nested groups extend that form, as in `todo.reminders.max`.\n\n## How do I control session sleep prevention?\n\n`power.sleepPrevention` defaults to `idle`. Set it to `off`, `idle`, `display`, or `system`; each\nlevel is cumulative and applies only while an agent session is active. xcsh maps the setting to\nIOKit on macOS, login1 and ScreenSaver on Linux, and thread-affine execution state on Windows.\n\n## How do I inspect the effective schema-backed values?\n\n```bash\nxcsh config list --json\nxcsh config get <KEY>\n```\n\nUse `xcsh config set <KEY> <VALUE>` only for a key present in the installed schema. Reset one key with `xcsh config reset <KEY>` rather than copying defaults from another release.\n\n## Which source defines a setting?\n\nThe settings schema and its tests in the matching source release define type, default, and validation. `xcsh config list --json` reports the value after user, project, environment, and command-line precedence is applied. When the two disagree, the schema describes what is legal and the effective output describes what is active.\n\nUnknown keys and wrong value types are configuration errors, even when a nearby subsystem has a\nsimilarly named option. Consult the generated schema for the installed version, place the key under\nits documented namespace, and restart the process after editing. Use `/settings` to inspect\neffective interactive values. When diagnosing precedence, remove the highest-priority override and\nrelaunch rather than editing several layers at once.\n",
52
52
  "en/reference-troubleshooting/diagnostics.mdx": "---\ntitle: \"Run diagnostics\"\ndescription: \"Collect version, configuration, provider, context, and sandbox evidence without exposing secrets.\"\nsidebar:\n order: 4\n label: \"Run diagnostics\"\n---\n\nCollect the smallest evidence set that identifies the failing layer. Never print credentials or raw context files.\n\n## The shell cannot find xcsh\n\nRun `command -v xcsh` and `xcsh --version`. If the path belongs to an old installation channel, remove that channel and reinstall once through the intended channel.\n\n## A model is unavailable or authentication fails\n\nRun `xcsh --list-models <PROVIDER>`. If no models appear, verify the relevant credential is present without echoing it. Retry with a provider-qualified `--model` name.\n\nFor Fable, `credits_required` means the authenticated Anthropic account is not entitled or funded\nfor that model; xcsh reports it once without retrying. Check Claude subscription access or API\nbilling for the credential in use. An obsolete-client error also does not retry: upgrade xcsh so\nits Claude Code compatibility header matches the current client, then try again.\n\n## A context or resource command fails\n\nConfirm `/context status` and the explicit namespace. Run `validate` to separate local manifest errors from live application programming interface (API) errors, then retry a read-only `get` or `diff`.\n\n## A tool or extension fails during startup\n\nStart from the smallest surface, then widen it:\n\n1. Run with `--no-extensions --no-rules`, adding `--no-pty` if the terminal itself is suspect.\n2. Re-enable one subsystem, then repeat the failing command.\n3. Check optional dependencies with `xcsh setup <COMPONENT> --check --json`.\n4. Check plugins with `xcsh plugin doctor`.\n",
53
53
  "en/reference-troubleshooting/index.mdx": "---\ntitle: \"Reference and troubleshooting\"\ndescription: \"Diagnose installations, providers, contexts, and tools, then consult current command and configuration references.\"\nsidebar:\n order: 0\n label: \"Reference\"\n---\n\nBegin with the observed symptom and isolate one layer at a time.\n\n## Where do I look?\n\n- [Diagnostics](/xcsh/en/reference-troubleshooting/diagnostics/) for installation, provider, context, and tool symptoms.\n- [command-line interface (CLI) reference](/xcsh/en/reference-troubleshooting/cli-reference/) for current command shapes.\n- [Slash commands and hotkeys](/xcsh/en/reference-troubleshooting/slash-keybindings/) for interactive discovery.\n- [Configuration schema](/xcsh/en/reference-troubleshooting/configuration-schema/) for supported settings.\n\nTerms are defined where they are used; there is no separate glossary.\n",
@@ -0,0 +1,209 @@
1
+ export type KnowledgeRoute = "api" | "documentation" | "direct-uri" | "none";
2
+ export type DocumentationKnowledgeSource = "docs-cloud-f5-com" | "my-f5-com";
3
+
4
+ export interface KnowledgeClassifierResource {
5
+ readonly name: string;
6
+ readonly aliases: readonly string[];
7
+ readonly domain: string;
8
+ readonly categories: readonly string[];
9
+ }
10
+
11
+ export interface KnowledgeSearchPlan {
12
+ readonly intent: string;
13
+ readonly lex: readonly string[];
14
+ readonly vec: readonly string[];
15
+ readonly hyde: readonly string[];
16
+ }
17
+
18
+ export interface KnowledgeConstraints {
19
+ readonly domain?: string;
20
+ readonly source?: DocumentationKnowledgeSource;
21
+ }
22
+
23
+ export interface KnowledgeClassification {
24
+ readonly route: KnowledgeRoute;
25
+ readonly confidence: number;
26
+ readonly query: KnowledgeSearchPlan;
27
+ readonly constraints: KnowledgeConstraints;
28
+ readonly resource?: string;
29
+ readonly domain?: string;
30
+ readonly source?: DocumentationKnowledgeSource;
31
+ readonly documentPath?: string;
32
+ readonly directUri?: string;
33
+ }
34
+
35
+ export interface KnowledgeClassifierOptions {
36
+ readonly toolsEnabled: boolean;
37
+ readonly resources?: readonly KnowledgeClassifierResource[];
38
+ readonly documentationSource?: DocumentationKnowledgeSource;
39
+ readonly previous?: KnowledgeClassification | null;
40
+ }
41
+
42
+ const EMPTY_QUERY: KnowledgeSearchPlan = { intent: "", lex: [], vec: [], hyde: [] };
43
+ const THIRD_PARTY_SCOPE = /\b(?:aws|amazon|azure|gcp|google cloud|kubernetes)\b/i;
44
+ const F5_SCOPE = /\b(?:f5(?:\s+distributed\s+cloud)?|xc)\b/i;
45
+ const ANAPHORIC = /\b(?:its|it|that|this|those|them|the same)\b/i;
46
+ const API_METADATA =
47
+ /\b(?:api|endpoint|operation\s*id|api\s+path|http\s+method|method|payload|request\s+body|required\s+fields?|enum|allowed\s+values?|constraints?|limits?|maximum|max(?:imum)?|minimum|min(?:imum)?|how\s+many|create|creates|creating|get|gets|list|lists|update|updates|replace|replaces|delete|deletes|clone|import)\b/i;
48
+ const DOCUMENTATION_INTENT =
49
+ /\b(?:documentation|docs?|guide|tutorial|how\s+(?:do|can|to)|configure|configuration|set\s*up|troubleshoot|procedure|instructions?)\b/i;
50
+ const EXCLUDED_API_INTENT = /\b(?:pricing|price|quote|licen[cs]ing|sales)\b/i;
51
+ const DIRECT_URI = /\bxcsh:\/\/(?:api-catalog|api-spec|documentation)\/[^\s)>\]}]*/i;
52
+
53
+ function normalizePhrase(value: string): string {
54
+ return value
55
+ .toLocaleLowerCase("en-US")
56
+ .replace(/[_-]+/g, " ")
57
+ .replace(/[^a-z0-9]+/g, " ")
58
+ .trim()
59
+ .replace(/\s+/g, " ");
60
+ }
61
+
62
+ function action(value: string): { base?: string; gerund?: string } {
63
+ if (/\b(?:create|creates|creating)\b/i.test(value)) return { base: "create", gerund: "creating" };
64
+ if (/\b(?:configure|configuration|configuring)\b/i.test(value)) return { base: "configure", gerund: "configuring" };
65
+ if (/\b(?:set\s*up|setting\s*up)\b/i.test(value)) return { base: "set up", gerund: "setting up" };
66
+ if (/\b(?:get|gets|read|fetch)\b/i.test(value)) return { base: "get", gerund: "getting" };
67
+ if (/\b(?:list|lists|listing)\b/i.test(value)) return { base: "list", gerund: "listing" };
68
+ if (/\b(?:update|updates|updating)\b/i.test(value)) return { base: "update", gerund: "updating" };
69
+ if (/\b(?:replace|replaces|replacing)\b/i.test(value)) return { base: "replace", gerund: "replacing" };
70
+ if (/\b(?:delete|deletes|remove|removing)\b/i.test(value)) return { base: "delete", gerund: "deleting" };
71
+ if (/\bclone\b/i.test(value)) return { base: "clone", gerund: "cloning" };
72
+ if (/\bimport\b/i.test(value)) return { base: "import", gerund: "importing" };
73
+ return {};
74
+ }
75
+
76
+ function documentationSubject(prompt: string): string {
77
+ const normalized = normalizePhrase(prompt);
78
+ const withoutPreamble = normalized
79
+ .replace(/^(?:please )?(?:use|read|find|show me|show) /, "")
80
+ .replace(/^(?:the )?(?:official |pinned )?f5 (?:distributed cloud )?(?:documentation|docs?) (?:to |for )?/, "")
81
+ .replace(/^(?:the )?(?:official |pinned )?(?:documentation|docs?) (?:to |for )?/, "");
82
+ return (withoutPreamble || normalized)
83
+ .replace(/\b(?:a|an|the)\b/g, " ")
84
+ .replace(/\s+/g, " ")
85
+ .trim();
86
+ }
87
+
88
+ export function buildKnowledgeSearchPlan(route: "api" | "documentation", subject: string): KnowledgeSearchPlan {
89
+ const normalized = normalizePhrase(subject);
90
+ const verb = action(normalized);
91
+ if (route === "api") {
92
+ const resource = verb.base
93
+ ? normalizePhrase(normalized.replace(new RegExp(`\\b${verb.base}\\b`), ""))
94
+ : normalized;
95
+ const lex = [...new Set([resource, verb.base ? `${verb.base} ${resource}` : resource].filter(Boolean))];
96
+ const phrase = verb.gerund ? `${verb.gerund} ${resource}` : resource;
97
+ return {
98
+ intent: `Find the authoritative F5 Distributed Cloud API operation for ${phrase}.`,
99
+ lex,
100
+ vec: [`${verb.base ? `${verb.base} ` : ""}${resource} F5 Distributed Cloud API`],
101
+ hyde: [
102
+ `An authoritative F5 Distributed Cloud API operation for ${phrase}, including method, path, operation ID, request fields, and constraints.`,
103
+ ],
104
+ };
105
+ }
106
+ const phrase = verb.gerund ? normalized.replace(new RegExp(`^${verb.base}`), verb.gerund) : normalized;
107
+ return {
108
+ intent: `Find the authoritative F5 documentation page for ${phrase}.`,
109
+ lex: [normalized],
110
+ vec: [`${subject.trim()} F5 documentation`],
111
+ hyde: [
112
+ `An authoritative F5 documentation page explains how to ${subject.trim()} with prerequisites, ordered steps, verification, and limitations.`,
113
+ ],
114
+ };
115
+ }
116
+
117
+ function none(): KnowledgeClassification {
118
+ return { route: "none", confidence: 0, query: EMPTY_QUERY, constraints: {} };
119
+ }
120
+
121
+ function direct(prompt: string): KnowledgeClassification | null {
122
+ const directUri = DIRECT_URI.exec(prompt)?.[0];
123
+ if (!directUri) return null;
124
+ try {
125
+ const parsed = new URL(directUri);
126
+ const constraints: KnowledgeConstraints = {};
127
+ if (parsed.hostname === "api-spec") {
128
+ const domain = parsed.pathname.split("/").filter(Boolean)[0];
129
+ if (domain) Object.assign(constraints, { domain });
130
+ }
131
+ if (parsed.hostname === "documentation") {
132
+ const source = parsed.pathname.split("/").filter(Boolean)[0];
133
+ if (source === "docs-cloud-f5-com" || source === "my-f5-com") Object.assign(constraints, { source });
134
+ }
135
+ return { route: "direct-uri", confidence: 1, directUri, constraints, query: EMPTY_QUERY };
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ function selectResource(
142
+ prompt: string,
143
+ resources: readonly KnowledgeClassifierResource[],
144
+ ): KnowledgeClassifierResource | undefined {
145
+ const normalizedPrompt = ` ${normalizePhrase(prompt)} `;
146
+ return resources
147
+ .flatMap(resource =>
148
+ resource.aliases
149
+ .map(alias => normalizePhrase(alias))
150
+ .filter(alias => alias && normalizedPrompt.includes(` ${alias} `))
151
+ .map(alias => ({ resource, alias })),
152
+ )
153
+ .sort(
154
+ (left, right) =>
155
+ right.alias.length - left.alias.length ||
156
+ right.resource.categories.length - left.resource.categories.length ||
157
+ left.resource.name.localeCompare(right.resource.name) ||
158
+ left.resource.domain.localeCompare(right.resource.domain),
159
+ )[0]?.resource;
160
+ }
161
+
162
+ export function classifyKnowledgeRequest(prompt: string, options: KnowledgeClassifierOptions): KnowledgeClassification {
163
+ if (!options.toolsEnabled) return none();
164
+ const directResult = direct(prompt);
165
+ if (directResult) return directResult;
166
+
167
+ const resources = options.resources ?? [];
168
+ const selected = selectResource(prompt, resources);
169
+ const thirdPartyOnly = THIRD_PARTY_SCOPE.test(prompt) && !F5_SCOPE.test(prompt);
170
+ if (!thirdPartyOnly && !EXCLUDED_API_INTENT.test(prompt) && API_METADATA.test(prompt)) {
171
+ const contextual =
172
+ !selected && ANAPHORIC.test(prompt) && options.previous?.route === "api" ? options.previous : null;
173
+ const resource = selected?.name ?? contextual?.resource;
174
+ const domain = selected?.domain ?? contextual?.domain;
175
+ if (resource && domain) {
176
+ const alias = normalizePhrase(selected?.aliases[0] ?? contextual?.query.lex[0] ?? resource);
177
+ const verb = action(prompt).base;
178
+ const subject = verb ? `${verb} ${alias}` : alias;
179
+ return {
180
+ route: "api",
181
+ confidence: F5_SCOPE.test(prompt) || contextual ? 0.99 : 0.97,
182
+ resource,
183
+ domain,
184
+ constraints: { domain },
185
+ query: buildKnowledgeSearchPlan("api", subject),
186
+ };
187
+ }
188
+ }
189
+
190
+ const previousDocumentation = options.previous?.route === "documentation" ? options.previous : null;
191
+ if (
192
+ !thirdPartyOnly &&
193
+ (DOCUMENTATION_INTENT.test(prompt) || (ANAPHORIC.test(prompt) && previousDocumentation)) &&
194
+ (F5_SCOPE.test(prompt) || /\b(?:documentation|docs?)\b/i.test(prompt) || previousDocumentation)
195
+ ) {
196
+ const source = options.documentationSource ?? previousDocumentation?.source;
197
+ const subject = documentationSubject(prompt);
198
+ return {
199
+ route: "documentation",
200
+ confidence: /\b(?:documentation|docs?)\b/i.test(prompt) ? 0.99 : 0.96,
201
+ source,
202
+ documentPath: previousDocumentation?.documentPath,
203
+ constraints: source ? { source } : {},
204
+ query: buildKnowledgeSearchPlan("documentation", subject),
205
+ };
206
+ }
207
+
208
+ return none();
209
+ }
@@ -0,0 +1,90 @@
1
+ import type { KnowledgeSearchPlan } from "./knowledge-classifier";
2
+
3
+ export type KnowledgeSearchMode = "bm25" | "hybrid";
4
+ export type KnowledgeSearchCorpus = "api" | "documentation";
5
+
6
+ export interface KnowledgeSearchResult {
7
+ readonly displayPath: string;
8
+ readonly score: number;
9
+ readonly [key: string]: unknown;
10
+ }
11
+
12
+ export interface KnowledgeSearchStore<T extends KnowledgeSearchResult = KnowledgeSearchResult> {
13
+ search(options: Record<string, unknown>): Promise<readonly T[]>;
14
+ searchLex(query: string, options?: { limit?: number; collection?: string | string[] }): Promise<readonly T[]>;
15
+ }
16
+
17
+ export interface KnowledgeSearchOptions {
18
+ readonly mode: KnowledgeSearchMode;
19
+ readonly corpus: KnowledgeSearchCorpus;
20
+ readonly collections?: readonly string[];
21
+ readonly limit: number;
22
+ readonly candidateLimit?: number;
23
+ readonly rerank?: boolean;
24
+ readonly explain?: boolean;
25
+ readonly minScore?: number;
26
+ }
27
+
28
+ export const KNOWLEDGE_SCORE_THRESHOLDS = {
29
+ api: 0.2,
30
+ documentation: 0.25,
31
+ } as const;
32
+
33
+ function interleave<T extends KnowledgeSearchResult>(lists: readonly (readonly T[])[], limit: number): T[] {
34
+ const results: T[] = [];
35
+ const seen = new Set<string>();
36
+ for (let position = 0; results.length < limit; position++) {
37
+ let found = false;
38
+ for (const list of lists) {
39
+ const result = list[position];
40
+ if (!result) continue;
41
+ found = true;
42
+ if (!seen.has(result.displayPath)) {
43
+ seen.add(result.displayPath);
44
+ results.push(result);
45
+ if (results.length === limit) break;
46
+ }
47
+ }
48
+ if (!found) break;
49
+ }
50
+ return results;
51
+ }
52
+
53
+ /**
54
+ * Runs a deterministic structured query. The BM25 mode never invokes model
55
+ * APIs; hybrid mode supplies typed queries so QMD never loads its expansion
56
+ * model. Native/model errors deliberately propagate to the caller.
57
+ */
58
+ export async function searchKnowledgeStore<T extends KnowledgeSearchResult>(
59
+ store: KnowledgeSearchStore<T>,
60
+ plan: KnowledgeSearchPlan,
61
+ options: KnowledgeSearchOptions,
62
+ ): Promise<readonly T[]> {
63
+ if (options.mode === "bm25") {
64
+ const lists: Array<readonly T[]> = [];
65
+ for (const query of plan.lex) {
66
+ lists.push(
67
+ await store.searchLex(query, {
68
+ limit: options.limit,
69
+ collection: options.collections ? [...options.collections] : undefined,
70
+ }),
71
+ );
72
+ }
73
+ return interleave(lists, options.limit);
74
+ }
75
+
76
+ return store.search({
77
+ queries: [
78
+ ...plan.lex.map(query => ({ type: "lex" as const, query })),
79
+ ...plan.vec.map(query => ({ type: "vec" as const, query })),
80
+ ...plan.hyde.map(query => ({ type: "hyde" as const, query })),
81
+ ],
82
+ intent: plan.intent,
83
+ collections: options.collections ? [...options.collections] : undefined,
84
+ limit: options.limit,
85
+ candidateLimit: options.candidateLimit ?? 30,
86
+ minScore: options.minScore ?? KNOWLEDGE_SCORE_THRESHOLDS[options.corpus],
87
+ explain: options.explain ?? true,
88
+ rerank: options.rerank ?? false,
89
+ });
90
+ }
@@ -29,7 +29,7 @@ This document is the authoritative answer when the user asks about xcsh itself.
29
29
 
30
30
  xcsh serves F5 Distributed Cloud sales engineers. Product documentation is
31
31
  federated across the f5-sales-demo GitHub organization. Entry point:
32
- https://f5-sales-demo.github.io/docs/llms.txt
32
+ https://f5-sales-demo.github.io/llms.txt
33
33
 
34
34
  Each product repo publishes: llms.txt (index with sidebar nav), custom sets
35
35
  at /_llms-txt/{topic}.txt, per-page content at /{slug}.md, plus
@@ -5,6 +5,15 @@ Select the best result and read its exact follow-up URL,
5
5
  `xcsh://documentation/<source>/<stable-path>/index.md`, before answering. Use the source filter when the question
6
6
  clearly targets `docs-cloud-f5-com` or `my-f5-com`.
7
7
 
8
+ Render documentation images only when the user explicitly asks to see them. Extract all unique Markdown image references
9
+ from the exact document, resolve relative references against its exact `xcsh://documentation/` URI, and
10
+ read each asset one at a time in document order. Static screenshot collections are not timelines or slideshows.
11
+ Pair every returned image with its official Markdown caption verbatim and one conservative explanation grounded in
12
+ the image, its caption, and nearby prose. If pixels are unavailable, use only the caption and nearby prose without
13
+ inventing visual details. Do not announce or estimate the image count before all reads finish. Afterward, verify that
14
+ the reported image count matches the number of successful asset reads. Documentation assets use `read`, even when
15
+ generic image-inspection guidance prefers `inspect_image`.
16
+
8
17
  For API paths, methods, payload fields, required status, enums, and constraints, use the deterministic
9
18
  `api-catalog-preflight` result followed by exact `xcsh://api-catalog/` and `xcsh://api-spec/` reads first.
10
19
  Do not route API metadata through the documentation QMD index.
@@ -13,7 +22,7 @@ Use the live cascade below only when the pinned documentation is stale or missin
13
22
  the QMD lookup fails. You **MUST** disclose why the pinned snapshot is stale, missing, or unavailable before the
14
23
  first live lookup:
15
24
 
16
- 1. **Federation index** — Read `https://f5-sales-demo.github.io/docs/llms.txt` and select the relevant categorized site.
25
+ 1. **Federation index** — Read `https://f5-sales-demo.github.io/llms.txt` and select the relevant categorized site.
17
26
  2. **Site index** — Read that site's `llms.txt`; use its Documentation Sets, Sections, and Translations links as published.
18
27
  3. **Locale index** — Prefer the user's published locale. The default locale's Section normally points to `/_llms-txt/en.txt`; locale-aware tiered paths use `/_llms-txt/{locale}/…`. If a localized endpoint is absent or contains only a system marker, fall back to English and disclose that fallback.
19
28
  4. **Focused content** — Follow `## Contents` links recursively until the narrowest leaf `.txt` answers the question. Generated `/_llms-txt/` links are canonical; do not rewrite them. A same-locale page endpoint such as `/{locale}/{slug}.md` is an equivalent leaf when a page URL is already known.
@@ -495,10 +495,23 @@ search `xcsh://documentation/?search=<query>` first, then read the exact
495
495
  `xcsh://documentation/<source>/<stable-path>/index.md` result before answering. The search result
496
496
  is discovery; the exact Markdown document is the evidence.
497
497
 
498
+ ### Documentation images
499
+
500
+ Render documentation images only when the user explicitly asks to see them. After that request, extract every
501
+ unique Markdown image reference from the exact document, resolve each relative reference against that document's
502
+ `xcsh://documentation/` URI, and preserve document order. Read each exact asset URI sequentially, one at a time, so
503
+ the transcript order is stable. Do not use `display_media` for documentation assets, and do not convert a static
504
+ screenshot collection into a timeline or slideshow.
505
+
506
+ Inspect each returned image conservatively. Present its official Markdown caption verbatim with one grounded explanation.
507
+ If image pixels are unavailable to the active model, quietly rely only on the official caption and nearby document
508
+ prose; never invent visual details. Do not announce or estimate the image count before all reads finish. Afterward,
509
+ verify that the reported image count matches the number of successful asset reads.
510
+
498
511
  If the pinned snapshot is stale, missing, or the QMD lookup fails, disclose that condition before using the live
499
512
  fallback knowledge index:
500
513
 
501
- `https://f5-sales-demo.github.io/docs/llms.txt`
514
+ `https://f5-sales-demo.github.io/llms.txt`
502
515
 
503
516
  Follow links from there to the relevant site's own `llms.txt`, then fetch only the
504
517
  narrowest published content that answers the question. Do not silently replace or supplement a successful
@@ -714,6 +727,7 @@ Don't open a file hoping. Hope is not a strategy.
714
727
  - For image understanding tasks: **MUST** use `inspect_image` over `read` to avoid overloading main session context.
715
728
  - Write a specific `question` for `inspect_image`: what to inspect, constraints (for example verbatim OCR), and desired output format.
716
729
  - If you encounter `[Image content detected but current model does not support vision]` in a message, use `inspect_image` with the image file path to analyze it. Do not ask the user to describe the image — analyze it yourself via the tool.
730
+ - Documentation images are the exception: use the exact `xcsh://documentation/` asset URI with `read`; do not pass internal documentation URIs to `inspect_image`.
717
731
  {{/if}}
718
732
  {{#ifAll (includes tools "inspect_image") (includes tools "generate_image")}}
719
733
  ### Image generation and analysis
@@ -6,3 +6,7 @@ Pass exactly one of:
6
6
  - `frames`: ordered text frames or raster-frame sources with per-frame `durationMs`.
7
7
 
8
8
  Playback is always muted. Autoplay defaults to once, looping is opt-in, and playback is capped at 12 FPS unless a lower or higher cap (up to 60) is requested. Unsupported terminals and missing FFmpeg receive a static poster or text fallback.
9
+
10
+ Do not use this tool for documentation assets. Resolve each Markdown image to its exact
11
+ `xcsh://documentation/<source>/<stable-path>/assets/<filename>` URI and use `read` sequentially. Static screenshot
12
+ collections are not timelines or slideshows.
@@ -21,7 +21,6 @@ export interface LlmsIndex {
21
21
 
22
22
  const ENTRY_PATTERN = /^- \[([^\]]+)\]\(([^)]+)\):\s*(.+)$/;
23
23
  const ROOT_LLMS_FILE = "llms.txt";
24
- const PORTAL_SLUG = "docs";
25
24
 
26
25
  function extractRootLlmsSlug(url: string): string | null {
27
26
  try {
@@ -64,8 +63,9 @@ function isValidIndex(value: unknown): value is LlmsIndex {
64
63
  *
65
64
  * Federation category headings are intentionally open-ended. A list entry is a
66
65
  * topic when it links to a sibling site's root /{slug}/llms.txt endpoint; this
67
- * excludes documentation sets, locale indexes, and tiered content without
68
- * hardcoding today's category names.
66
+ * excludes documentation sets and tiered content. The root portal now has
67
+ * locale feeds at /{locale}/llms.txt, so translation entries are excluded
68
+ * by their heading without restricting future federation categories.
69
69
  */
70
70
  export function parseLlmsTxt(content: string, now?: Date): LlmsIndex {
71
71
  const lines = content.split("\n");
@@ -94,11 +94,11 @@ export function parseLlmsTxt(content: string, now?: Date): LlmsIndex {
94
94
  }
95
95
 
96
96
  const match = ENTRY_PATTERN.exec(trimmed);
97
- if (!match || !category) continue;
97
+ if (!match || !category || category === "Translations") continue;
98
98
 
99
99
  const [, name, url, topicDescription] = match;
100
100
  const slug = extractRootLlmsSlug(url);
101
- if (!slug || slug === PORTAL_SLUG || seenUrls.has(url)) continue;
101
+ if (!slug || seenUrls.has(url)) continue;
102
102
 
103
103
  seenUrls.add(url);
104
104
  topics.push({ name, description: topicDescription, url, category });
@@ -113,7 +113,7 @@ export function parseLlmsTxt(content: string, now?: Date): LlmsIndex {
113
113
  };
114
114
  }
115
115
 
116
- const ROOT_LLMS_URL = "https://f5-sales-demo.github.io/docs/llms.txt";
116
+ const ROOT_LLMS_URL = "https://f5-sales-demo.github.io/llms.txt";
117
117
  const DEFAULT_TTL_MS = 3_600_000;
118
118
 
119
119
  export class KnowledgeService {