{
  "toolkit_version": "1.1.0",
  "studio_min_release": "1.0.121",
  "description": "AlphaAgent document & media toolkit. Installed by alphaagent-env-toolkit.sh into every execution-environment image (ours and customers'). The runtime probe reads the copy at /opt/alphaagent/toolkit.lock for toolkit_version and runs `checks`.",
  "check_semantics": "Each check runs `cmd` with `/bin/sh -c`, offline, as the image's runtime user. It passes when the exit status is 0 AND `expect` (a Python `re.search` pattern) matches the combined stdout+stderr (stderr matters: pdftoppm -v and dot -V print to stderr). Checks with `needs_browser: true` launch Chromium; Chromium cannot run under QEMU user-mode emulation (an amd64 image built on an arm64 laptop), so when `uname -m` is x86_64 and /proc/cpuinfo has no x86 `flags:` line those checks are reported `skipped` with a reason instead of pass/fail and do not affect the overall `ok`. On real hardware (CI, Lambda) nothing is ever skipped. `alphaagent-env-toolkit.sh --check` and the Studio probe apply exactly these rules. Timeouts: the Studio probe gives each check `check_timeout_s` (default 15) and each `needs_browser` check `browser_check_timeout_s` (default 60; a cold Chromium in Lambda needs well over 15 s). Both `--check` and the probe run every check with RLIMIT_CORE soft=0 and, when $HOME is not readable+writable, with HOME/XDG_CACHE_HOME/TMPDIR redirected under /tmp - exactly the environment the Lambda handler gives every exec. `--check --lambda-like` is the release gate: run from OUTSIDE the image as `docker run --rm --user 993:990 --read-only --tmpfs /tmp:size=512m -e HOME=/home/agent <image> /opt/alphaagent/toolkit/alphaagent-env-toolkit.sh --check --lambda-like`; it reports a `lambda_like` block (uid, HOME readability, /tmp, core dumps) and exits 1 when `toolkit_ok` is false. The final line on stdout is the compact JSON result.",
  "apt": [
    "ca-certificates",
    "fontconfig",
    "unzip",
    "libreoffice-writer",
    "libreoffice-calc",
    "libreoffice-impress",
    "libreoffice-common",
    "poppler-utils",
    "pandoc",
    "graphviz",
    "fonts-dejavu",
    "fonts-liberation",
    "fonts-noto-core",
    "libpango-1.0-0",
    "libpangoft2-1.0-0",
    "libpangocairo-1.0-0",
    "libcairo2",
    "libgdk-pixbuf-2.0-0|libgdk-pixbuf2.0-0",
    "libffi8|libffi7",
    "libnss3",
    "libnspr4",
    "libatk1.0-0t64|libatk1.0-0",
    "libatk-bridge2.0-0t64|libatk-bridge2.0-0",
    "libatspi2.0-0t64|libatspi2.0-0",
    "libcups2t64|libcups2",
    "libglib2.0-0t64|libglib2.0-0",
    "libasound2t64|libasound2",
    "libdbus-1-3",
    "libdrm2",
    "libgbm1",
    "libexpat1",
    "libxkbcommon0",
    "libxcomposite1",
    "libxdamage1",
    "libxfixes3",
    "libxrandr2",
    "libxshmfence1",
    "libx11-6",
    "libxcb1",
    "libxext6"
  ],
  "pip": [
    "reportlab==5.0.1",
    "weasyprint==70.0",
    "python-pptx==1.0.2",
    "python-docx==1.2.0",
    "openpyxl==3.1.5",
    "xlsxwriter==3.2.9",
    "pypdf==6.19.0",
    "pdfplumber==0.11.10",
    "matplotlib==3.11.2",
    "plotly==7.1.0",
    "kaleido==1.4.0",
    "markdown==3.10.3",
    "Pillow==12.3.0"
  ],
  "node": {
    "version": "22.23.2",
    "install_prefix": "/usr/local",
    "tarballs": {
      "x86_64": {
        "url": "https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.gz",
        "sha256": "b294a556e639d64338823920e5866c21c02741742d2e1529ee1a225c1ec9252a"
      },
      "aarch64": {
        "url": "https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-arm64.tar.gz",
        "sha256": "013b59cfd2819703a6f4a14ab891fc46fc2a4e3f5bcd92de3fb4929b43e35b30"
      }
    }
  },
  "npm": [
    "puppeteer@25.11.0",
    "@mermaid-js/mermaid-cli@11.17.0",
    "pptxgenjs@4.0.1"
  ],
  "chromium": {
    "provider": "puppeteer",
    "cache_dir": "/opt/puppeteer",
    "executable_link": "/opt/alphaagent/chrome",
    "kaleido_wrapper": "/opt/alphaagent/chrome-kaleido",
    "puppeteer_config": "/opt/alphaagent/puppeteer-config.json",
    "args_note": "Lambda-safe launch flags (proven live on the env Lambda, x86_64, no /dev/shm, no user namespaces): the 1.0.0 set (no --no-zygote/--single-process) crashed Chrome in ~1 s and left ~16 x 82 MB core dumps per attempt. Never put --user-data-dir here: the mmdc wrapper creates a fresh one per run under $TMPDIR and choreographer creates its own.",
    "args": [
      "--no-sandbox",
      "--no-zygote",
      "--single-process",
      "--disable-gpu",
      "--disable-dev-shm-usage",
      "--disable-setuid-sandbox",
      "--disable-software-rasterizer"
    ],
    "kaleido_args": [
      "--no-sandbox",
      "--no-zygote",
      "--single-process",
      "--disable-gpu",
      "--disable-dev-shm-usage",
      "--disable-setuid-sandbox",
      "--disable-software-rasterizer"
    ]
  },
  "fonts_dir": "/usr/share/fonts/truetype/alphaagent",
  "fonts": [
    {
      "family": "IBM Plex Sans",
      "license": "OFL-1.1",
      "source": "github.com/IBM/plex release @ibm/plex-sans@1.1.0",
      "url": "https://github.com/IBM/plex/releases/download/%40ibm/plex-sans%401.1.0/ibm-plex-sans.zip",
      "sha256": "fb365d910566e6d199cc2c15579a7dd9a267128e18431a394ed81f1970c69200",
      "extract": [
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-Regular.ttf",
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-Italic.ttf",
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-Medium.ttf",
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-MediumItalic.ttf",
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-SemiBold.ttf",
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-SemiBoldItalic.ttf",
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-Bold.ttf",
        "ibm-plex-sans/fonts/complete/ttf/IBMPlexSans-BoldItalic.ttf",
        "ibm-plex-sans/LICENSE.txt"
      ]
    },
    {
      "family": "IBM Plex Mono",
      "license": "OFL-1.1",
      "source": "github.com/google/fonts @ e44c4b011a820c2cbe2fd2cfa8052037d7edb571",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/ibmplexmono/IBMPlexMono-Regular.ttf",
      "sha256": "6a3412f058c7d8dfd9170c41e85ade48e5156ecb89356110ca57a0a27734af46",
      "file": "IBMPlexMono-Regular.ttf"
    },
    {
      "family": "IBM Plex Mono",
      "license": "OFL-1.1",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/ibmplexmono/IBMPlexMono-Bold.ttf",
      "sha256": "ac27abd6450a64dd94467580a02fe6235156d5b92f2926ebbc8e7489df64e0be",
      "file": "IBMPlexMono-Bold.ttf"
    },
    {
      "family": "IBM Plex Mono",
      "license": "OFL-1.1",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/ibmplexmono/IBMPlexMono-Italic.ttf",
      "sha256": "3362fc791b0652193328b862c1c5f23a789bc7288b1617fa63302f88689a2a34",
      "file": "IBMPlexMono-Italic.ttf"
    },
    {
      "family": "IBM Plex Mono",
      "license": "OFL-1.1",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/ibmplexmono/IBMPlexMono-BoldItalic.ttf",
      "sha256": "af4e05a761e98c1adf064c48a6352c9bec1a6ad70982cd2a544149323391f98e",
      "file": "IBMPlexMono-BoldItalic.ttf"
    },
    {
      "family": "IBM Plex Mono",
      "license": "OFL-1.1",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/ibmplexmono/OFL.txt",
      "sha256": "7e6b2818edbd8f6a01ae80641cc8f16a51080d08fb4e532be3a0b6f74adb07da",
      "file": "IBMPlexMono-OFL.txt"
    },
    {
      "family": "Raleway",
      "license": "OFL-1.1",
      "source": "github.com/google/fonts @ e44c4b011a820c2cbe2fd2cfa8052037d7edb571 (variable font, wght axis)",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/raleway/Raleway%5Bwght%5D.ttf",
      "sha256": "8bbcc3eb8275c388f4bcd998832f8a4b943eadbaf6a595205312774b5951aefb",
      "file": "Raleway[wght].ttf"
    },
    {
      "family": "Raleway",
      "license": "OFL-1.1",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/raleway/Raleway-Italic%5Bwght%5D.ttf",
      "sha256": "96629caf2202183fab46c70237055a7d67e6a5400b85413d45a77ed6f2a0770c",
      "file": "Raleway-Italic[wght].ttf"
    },
    {
      "family": "Raleway",
      "license": "OFL-1.1",
      "url": "https://raw.githubusercontent.com/google/fonts/e44c4b011a820c2cbe2fd2cfa8052037d7edb571/ofl/raleway/OFL.txt",
      "sha256": "7e946cf1171784d1015279e7dc35f827957a6b5d1f1f659ae0c98e5f5e37ed9b",
      "file": "Raleway-OFL.txt"
    }
  ],
  "checks": [
    {
      "name": "soffice",
      "cmd": "soffice --version",
      "expect": "LibreOffice"
    },
    {
      "name": "pdftoppm",
      "cmd": "pdftoppm -v",
      "expect": "pdftoppm version"
    },
    {
      "name": "pandoc",
      "cmd": "pandoc -v",
      "expect": "^pandoc"
    },
    {
      "name": "graphviz",
      "cmd": "dot -V",
      "expect": "graphviz version"
    },
    {
      "name": "weasyprint",
      "cmd": "python3 -c \"import weasyprint; print('weasyprint', weasyprint.__version__)\"",
      "expect": "weasyprint [0-9]"
    },
    {
      "name": "doc-libs",
      "cmd": "python3 -c \"import reportlab, pptx, docx, openpyxl, xlsxwriter, pypdf, pdfplumber, matplotlib, plotly, kaleido, markdown, PIL; print('doc-libs ok')\"",
      "expect": "doc-libs ok"
    },
    {
      "name": "node",
      "cmd": "node -v",
      "expect": "^v22\\."
    },
    {
      "name": "mmdc",
      "cmd": "mmdc -V",
      "expect": "^11\\."
    },
    {
      "name": "mermaid-render",
      "cmd": "d=$(mktemp -d) && printf 'graph TD; A-->B\\n' > \"$d/t.mmd\" && mmdc -q -i \"$d/t.mmd\" -o \"$d/t.svg\" && test -s \"$d/t.svg\" && echo mermaid-render ok",
      "expect": "mermaid-render ok",
      "needs_browser": true
    },
    {
      "name": "pptxgenjs",
      "cmd": "NODE_PATH=\"${NODE_PATH:-$(npm root -g)}\" node -e \"require('pptxgenjs'); console.log('pptxgenjs ok')\"",
      "expect": "pptxgenjs ok"
    },
    {
      "name": "kaleido-render",
      "cmd": "python3 -c \"import os, tempfile, plotly.graph_objects as go; p=os.path.join(tempfile.mkdtemp(), 'k.png'); go.Figure(go.Bar(x=[1, 2], y=[3, 4])).write_image(p); print('kaleido-render ok' if os.path.getsize(p) > 0 else 'kaleido-render empty')\"",
      "expect": "kaleido-render ok",
      "needs_browser": true
    },
    {
      "name": "fonts-plex",
      "cmd": "fc-list | grep -i 'IBM Plex'",
      "expect": "IBM Plex (Sans|Mono)"
    },
    {
      "name": "fonts-raleway",
      "cmd": "fc-list | grep -i raleway",
      "expect": "Raleway"
    }
  ],
  "check_timeout_s": 15,
  "browser_check_timeout_s": 60
}
