@f5-sales-demo/xcsh 22.4.2 → 22.4.5

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.4.2",
21
- "commit": "d0215416d1a292f96f3e30d641260730fb2233bd",
22
- "shortCommit": "d021541",
20
+ "version": "22.4.5",
21
+ "commit": "88cedf5e2812ed04e6ce255c7a109c18f05635b7",
22
+ "shortCommit": "88cedf5",
23
23
  "branch": "main",
24
- "tag": "v22.4.2",
25
- "commitDate": "2026-09-29T12:29:41Z",
26
- "buildDate": "2026-09-29T13:06:20.087Z",
24
+ "tag": "v22.4.5",
25
+ "commitDate": "2026-09-30T00:55:42Z",
26
+ "buildDate": "2026-09-30T01:34:17.001Z",
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/d0215416d1a292f96f3e30d641260730fb2233bd",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v22.4.2"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/88cedf5e2812ed04e6ce255c7a109c18f05635b7",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v22.4.5"
33
33
  };
@@ -40,7 +40,7 @@ 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 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",
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>\n It installs the latest prebuilt release and a schema-v2 provenance receipt into <code>~/.local/bin</code>.\n The native addons are embedded in the executable.\n </p>\n <p>\n On Linux, the installer also retains the published SHA-256 checksums and build provenance,\n and verifies them before running or replacing the binary.\n A failed download, validation, or replacement restores the previous installation.\n </p>\n <p>\n The binary installer requires cURL, <code>jq</code>, and <code>sha256sum</code> or <code>shasum</code>.\n This is the only channel that <code>xcsh self-update</code> replaces in process.\n Need a managed or platform-specific channel? Compare the <a href=\"#installation-options\">installation options</a>.\n </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",
@@ -30,12 +30,13 @@ export const TERRAFORM_INDEX: TerraformIndex = {
30
30
  name: "Uncategorized",
31
31
  slug: "uncategorized",
32
32
  description: "Resources pending categorization",
33
- resource_count: 33,
33
+ resource_count: 34,
34
34
  resources: [
35
35
  "healthcheck",
36
36
  "application_profiles",
37
37
  "authorization_server",
38
38
  "bot_infrastructure",
39
+ "cloud_user_account",
39
40
  "cluster",
40
41
  "cminstance",
41
42
  "crl",
@@ -322,6 +323,20 @@ export const TERRAFORM_INDEX: TerraformIndex = {
322
323
  },
323
324
  import_syntax: "terraform import xcsh_bot_infrastructure.example namespace/name",
324
325
  },
326
+ {
327
+ name: "cloud_user_account",
328
+ category: "uncategorized",
329
+ description: "Cloud user account object create specifications. configuration",
330
+ required: ["name", "namespace"],
331
+ minimal_config: {
332
+ format: "terraform",
333
+ source: "_llms-txt/resources/cloud_user_account.txt#minimal-valid-config",
334
+ },
335
+ dependencies: {
336
+ requires: [],
337
+ },
338
+ import_syntax: "terraform import xcsh_cloud_user_account.example namespace/name",
339
+ },
325
340
  {
326
341
  name: "cluster",
327
342
  category: "uncategorized",
@@ -2,7 +2,12 @@ import { Container, Markdown, type TUI } from "@f5-sales-demo/pi-tui";
2
2
  import { replaceTabs } from "../../tools/render-utils";
3
3
  import { getMarkdownTheme, theme } from "../theme/theme";
4
4
  import { appInterruptHint } from "../utils/keybinding-matchers";
5
- import { selectorCancelHint, selectorFrame, selectorFrameContentWidth, selectorProse } from "./selector-frame";
5
+ import {
6
+ selectorCancelHint,
7
+ selectorFrameContentWidth,
8
+ selectorProse,
9
+ selectorTranscriptFrame,
10
+ } from "./selector-frame";
6
11
 
7
12
  type BtwPanelState = "running" | "complete" | "aborted" | "error";
8
13
 
@@ -69,18 +74,15 @@ export class BtwPanelComponent extends Container {
69
74
  ? new Markdown(answer, 0, 0, getMarkdownTheme()).render(innerWidth)
70
75
  : [theme.fg("dim", this.#state === "running" ? "Waiting for response…" : "No text returned.")];
71
76
  const state = this.#stateLine();
72
- return selectorFrame(
77
+ return selectorTranscriptFrame(
73
78
  width,
74
- body.length + 12,
75
79
  "BTW",
76
80
  "Ephemeral side answer from a snapshot of the current session; it is not added to the transcript.",
77
- [],
78
81
  [
79
82
  selectorProse(theme.fg("contentAccent", replaceTabs(this.#question))),
80
83
  ...body.map(line => selectorProse(line)),
81
84
  ],
82
- state ? [state] : [],
83
- [this.#footerLine()],
85
+ [this.#footerLine(), ...(state ? [state] : [])],
84
86
  );
85
87
  }
86
88
 
@@ -1,6 +1,6 @@
1
1
  import { Container, Markdown } from "@f5-sales-demo/pi-tui";
2
2
  import { getMarkdownTheme } from "../theme/theme";
3
- import { selectorFrame, selectorFrameContentWidth, selectorProse } from "./selector-frame";
3
+ import { selectorFrameContentWidth, selectorProse, selectorTranscriptFrame } from "./selector-frame";
4
4
 
5
5
  /** Static transcript-adjacent plan preview; approval controls live in the separate review overlay. */
6
6
  export class PlanPreviewComponent extends Container {
@@ -11,15 +11,11 @@ export class PlanPreviewComponent extends Container {
11
11
  override render(width: number): string[] {
12
12
  const inner = selectorFrameContentWidth(width);
13
13
  const body = new Markdown(this.content, 0, 0, getMarkdownTheme()).render(inner);
14
- return selectorFrame(
14
+ return selectorTranscriptFrame(
15
15
  width,
16
- body.length + 7,
17
16
  "Plan review",
18
17
  "Prepared execution plan; use the active review overlay to approve, revise, or cancel.",
19
- [],
20
18
  body.map(line => selectorProse(line)),
21
- [],
22
- [],
23
19
  );
24
20
  }
25
21
  }
@@ -1,7 +1,7 @@
1
1
  import type { Component } from "@f5-sales-demo/pi-tui";
2
2
  import type { AsyncInputQuestion, AsyncQuestionItem } from "../../../../chat-ui/src/interactions/contract";
3
3
  import { theme } from "../theme/theme";
4
- import { selectorFrame, selectorFrameContentWidth, selectorProse, selectorRow } from "./selector-frame";
4
+ import { selectorProse, selectorTranscriptFrame } from "./selector-frame";
5
5
 
6
6
  export interface AsyncInputReply {
7
7
  type: "user_input_reply";
@@ -97,19 +97,13 @@ export class QuestionTranscriptComponent implements Component {
97
97
  }
98
98
 
99
99
  render(width: number): string[] {
100
- const inner = selectorFrameContentWidth(width);
101
100
  if (this.state.kind === "pending") {
102
101
  const count = this.state.item.questions.length;
103
- return selectorFrame(
102
+ return selectorTranscriptFrame(
104
103
  width,
105
- Math.max(8, count + 7),
106
104
  "Questions pending",
107
105
  `${count} ${count === 1 ? "question" : "questions"} · Work continues while you answer`,
108
- [],
109
- this.state.item.questions.map((question, index) =>
110
- selectorRow([`${index + 1}. ${question.title}`], [inner - 2], false),
111
- ),
112
- [],
106
+ this.state.item.questions.map((question, index) => selectorProse(`${index + 1}. ${question.title}`)),
113
107
  [`${theme.fg("contentAccent", "/questions")} to answer`],
114
108
  );
115
109
  }
@@ -117,16 +111,10 @@ export class QuestionTranscriptComponent implements Component {
117
111
  const answer = question.isSecret
118
112
  ? theme.fg("muted", "Answer hidden")
119
113
  : reply.answer.replace(/^user_note:\s*/, "");
120
- return selectorFrame(
121
- width,
122
- 8,
123
- "Answer recorded",
124
- `Question ${position + 1} of ${total}`,
125
- [question.title],
126
- [selectorProse(`${theme.symbol("checkbox.checked")} ${answer}`)],
127
- [],
128
- [],
129
- );
114
+ return selectorTranscriptFrame(width, "Answer recorded", `Question ${position + 1} of ${total}`, [
115
+ selectorProse(question.title),
116
+ selectorProse(`${theme.symbol("checkbox.checked")} ${answer}`),
117
+ ]);
130
118
  }
131
119
 
132
120
  invalidate(): void {}
@@ -5,9 +5,11 @@ import {
5
5
  type SelectListTheme,
6
6
  type SgrMouseEvent,
7
7
  truncateToWidth,
8
+ wrapTextWithAnsi,
8
9
  } from "@f5-sales-demo/pi-tui";
9
10
  import {
10
11
  matchesSelectorKey,
12
+ SelectorDetailPager,
11
13
  selectorCancelHint,
12
14
  selectorFrame,
13
15
  selectorFrameContentWidth,
@@ -28,6 +30,7 @@ export class SearchableSelectList extends SelectList {
28
30
  #searchOrigin = 0;
29
31
  #visible = 1;
30
32
  #hitRows = new Map<number, number>();
33
+ #details = new SelectorDetailPager();
31
34
 
32
35
  constructor(
33
36
  private readonly title: string,
@@ -71,6 +74,16 @@ export class SearchableSelectList extends SelectList {
71
74
  const start = Math.max(0, Math.min(position - Math.floor(this.#visible / 2), filtered.length - this.#visible));
72
75
  const visible = filtered.slice(start, start + this.#visible);
73
76
  const selected = this.sourceItems[this.#selectedIndex];
77
+ const details = this.#details.render(
78
+ [
79
+ ...(wrapTextWithAnsi(this.purpose, inner).length > 2 ? [this.purpose] : []),
80
+ selected?.label ?? "",
81
+ selected?.description ?? "",
82
+ selected && selected.value !== selected.label ? `Identity: ${selected.value}` : "",
83
+ ],
84
+ inner,
85
+ Math.max(1, Math.floor(rows / 5)),
86
+ );
74
87
  const body = visible.length
75
88
  ? visible.map(index => {
76
89
  const item = this.sourceItems[index]!;
@@ -84,17 +97,16 @@ export class SearchableSelectList extends SelectList {
84
97
  this.purpose,
85
98
  [`${filtered.length} of ${this.sourceItems.length} options`, ...this.#search.render(inner)],
86
99
  body,
87
- selected
88
- ? [selected.description ?? "", selected.value !== selected.label ? `Identity: ${selected.value}` : ""]
89
- : [],
100
+ details,
90
101
  [
91
102
  selectorNavigationHint(),
92
103
  selectorCancelHint(this.#search.getValue() ? "clear search" : "back"),
93
- ...(filtered.length > this.#visible
104
+ ...this.#details.hint,
105
+ ...(this.#details.hint.length === 0 && filtered.length > this.#visible
94
106
  ? [`${selectorKeys("pageUp")}/${selectorKeys("pageDown")}: options`]
95
107
  : []),
96
108
  ],
97
- { selectedBodyIndex: visible.indexOf(this.#selectedIndex) },
109
+ { selectedBodyIndex: visible.indexOf(this.#selectedIndex), selectedDetail: "provided" },
98
110
  );
99
111
  this.#hitRows.clear();
100
112
  let cursor = 0;
@@ -161,7 +173,9 @@ export class SearchableSelectList extends SelectList {
161
173
  return;
162
174
  }
163
175
  if (matchesSelectorKey(data, "pageUp") || matchesSelectorKey(data, "pageDown")) {
164
- this.#move((matchesSelectorKey(data, "pageUp") ? -1 : 1) * this.#visible);
176
+ const direction = matchesSelectorKey(data, "pageUp") ? -1 : 1;
177
+ if (this.#details.page(direction)) return;
178
+ this.#move(direction * this.#visible);
165
179
  return;
166
180
  }
167
181
  if (matchesSelectorKey(data, "confirm")) {
@@ -243,6 +243,23 @@ export function selectorProse(content: string, tone: "text" | "muted" = "text"):
243
243
  return { kind: "wrapped-prose", content, tone };
244
244
  }
245
245
 
246
+ /** Static transcript content scrolls with the transcript rather than a bounded overlay. */
247
+ export function selectorTranscriptFrame(
248
+ width: number,
249
+ title: string,
250
+ purpose: string,
251
+ body: SelectorFrameLine[],
252
+ footer: string[] = [],
253
+ ): string[] {
254
+ const prose = [...(purpose ? [selectorProse(purpose, "muted")] : []), ...body];
255
+ const inner = selectorFrameContentWidth(width);
256
+ const bodyRows = prose.reduce(
257
+ (count, value) => count + (value.kind === "wrapped-prose" ? wrapSection([value.content], inner).length : 1),
258
+ 0,
259
+ );
260
+ return selectorFrame(width, bodyRows + wrapSection(footer, inner).length + 8, title, "", [], prose, [], footer);
261
+ }
262
+
246
263
  export function selectorCompactRow(
247
264
  content: string,
248
265
  selected = false,
@@ -401,8 +418,46 @@ export class ReportDetailsComponent extends Container {
401
418
  }
402
419
  }
403
420
 
421
+ /** Reusable viewport for complete selected prose without changing the selected item. */
422
+ export class SelectorDetailPager {
423
+ #offset = 0;
424
+ #capacity = 1;
425
+ #length = 0;
426
+ #source = "";
427
+
428
+ reset(): void {
429
+ this.#offset = 0;
430
+ }
431
+
432
+ render(text: string[], width: number, capacity: number): string[] {
433
+ const source = text.join("\n");
434
+ if (source !== this.#source) this.reset();
435
+ this.#source = source;
436
+ const lines = wrapSection(text, width);
437
+ this.#length = lines.length;
438
+ this.#capacity = Math.max(1, capacity);
439
+ this.#offset = Math.min(this.#offset, Math.max(0, lines.length - this.#capacity));
440
+ return lines.slice(this.#offset, this.#offset + this.#capacity);
441
+ }
442
+
443
+ page(direction: -1 | 1): boolean {
444
+ if (this.#length <= this.#capacity) return false;
445
+ this.#offset = Math.max(0, Math.min(this.#length - this.#capacity, this.#offset + direction * this.#capacity));
446
+ return true;
447
+ }
448
+
449
+ get hint(): string[] {
450
+ return this.#length > this.#capacity
451
+ ? [
452
+ `${selectorKeys("pageUp")}/${selectorKeys("pageDown")}: details ${this.#offset + 1}–${Math.min(this.#length, this.#offset + this.#capacity)} of ${this.#length}`,
453
+ ]
454
+ : [];
455
+ }
456
+ }
457
+
404
458
  export class ConnectionChoiceComponent extends Container {
405
459
  #selected = 0;
460
+ #details = new SelectorDetailPager();
406
461
  constructor(
407
462
  private title: string,
408
463
  private purpose: string,
@@ -415,19 +470,32 @@ export class ConnectionChoiceComponent extends Container {
415
470
  }
416
471
  override render(width: number): string[] {
417
472
  const contentWidth = selectorFrameContentWidth(width);
473
+ const rows = this.rows();
474
+ const choice = this.choices[this.#selected];
475
+ const details = this.#details.render(
476
+ [
477
+ ...(wrapSection([this.purpose], contentWidth).length > 2 ? [this.purpose] : []),
478
+ choice?.label ?? "",
479
+ choice?.description ?? "",
480
+ ],
481
+ contentWidth,
482
+ Math.max(1, Math.floor(rows / 5)),
483
+ );
418
484
  return selectorFrame(
419
485
  width,
420
- this.rows(),
486
+ rows,
421
487
  this.title,
422
488
  this.purpose,
423
489
  [],
424
490
  this.choices.map((choice, index) => selectorRow([choice.label], [contentWidth - 2], index === this.#selected)),
425
- [this.choices[this.#selected]?.description ?? ""],
426
- [selectorNavigationHint(), selectorCancelHint()],
427
- { selectedBodyIndex: this.#selected },
491
+ details,
492
+ [selectorNavigationHint(), selectorCancelHint(), ...this.#details.hint],
493
+ { selectedBodyIndex: this.#selected, selectedDetail: "provided" },
428
494
  );
429
495
  }
430
496
  handleInput(data: string): void {
497
+ if (matchesSelectorKey(data, "pageUp") && this.#details.page(-1)) return;
498
+ if (matchesSelectorKey(data, "pageDown") && this.#details.page(1)) return;
431
499
  if (matchesSelectorKey(data, "cancel")) this.onCancel();
432
500
  else if (matchesSelectorKey(data, "up"))
433
501
  this.#selected = (this.#selected - 1 + this.choices.length) % this.choices.length;
@@ -1,5 +1,10 @@
1
1
  import { type Component, Container } from "@f5-sales-demo/pi-tui";
2
- import { selectorCompactRow, selectorFrame, selectorFrameContentWidth, selectorProse } from "./selector-frame";
2
+ import {
3
+ selectorCompactRow,
4
+ selectorFrameContentWidth,
5
+ selectorProse,
6
+ selectorTranscriptFrame,
7
+ } from "./selector-frame";
3
8
 
4
9
  /** Frame arbitrary static transcript-adjacent content, including OSC links. */
5
10
  export class TranscriptComponentFrame extends Container {
@@ -14,18 +19,12 @@ export class TranscriptComponentFrame extends Container {
14
19
 
15
20
  override render(width: number): string[] {
16
21
  const body = this.content.render(selectorFrameContentWidth(width));
17
- return selectorFrame(
22
+ return selectorTranscriptFrame(
18
23
  width,
19
- body.length + this.footer.length + 7,
20
24
  this.title,
21
25
  this.purpose,
22
- [],
23
26
  body.map(line => selectorCompactRow(line)),
24
- [],
25
27
  this.footer,
26
- {
27
- maxBodyRows: Math.max(1, body.length),
28
- },
29
28
  );
30
29
  }
31
30
  }
@@ -42,18 +41,11 @@ export class TranscriptNoticeComponent extends Container {
42
41
 
43
42
  override render(width: number): string[] {
44
43
  const body = this.content.split("\n").filter(line => Bun.stripANSI(line).trim());
45
- return selectorFrame(
44
+ return selectorTranscriptFrame(
46
45
  width,
47
- body.length + 7,
48
46
  this.title,
49
47
  this.purpose,
50
- [],
51
48
  body.map(line => selectorProse(line)),
52
- [],
53
- [],
54
- {
55
- maxBodyRows: Math.max(1, body.length),
56
- },
57
49
  );
58
50
  }
59
51
  }