AG-UI 1.0: schema-first generation, and the enforcement pipeline that follows from it #3362
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: unit | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "sdks/typescript/**" | |
| - "typescript-sdk/**" | |
| - "integrations/**" | |
| - "middlewares/**" | |
| # `pnpm run test` is `nx run-many -t test`, which includes @ag-ui/spec — but | |
| # only once this workflow is triggered, so a change confined to the schema, | |
| # a fixture or the harness would otherwise merge without the suite running. | |
| - "spec/**" | |
| # The spec suite's drift gate also compares the generated Python and C# | |
| # files byte for byte, so a hand edit confined to them must trigger it | |
| # too; the proto suite additionally decodes the committed .NET byte | |
| # fixtures. | |
| - "sdks/python/ag_ui/_generated/**" | |
| - "sdks/dotnet/src/AGUI.Abstractions/Generated/**" | |
| - "sdks/dotnet/src/AGUI.Protobuf/Generated/**" | |
| - "sdks/dotnet/tests/AGUI.Protobuf.UnitTests/Fixtures/**" | |
| # The published schema is a generated file under the same drift gate, and | |
| # the docs folder is the one place it can be edited or deleted without | |
| # touching spec/ at all. | |
| - "docs/spec/**" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - "package.json" | |
| - "nx.json" | |
| - ".github/workflows/unit-typescript-sdk.yml" | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - "sdks/typescript/**" | |
| - "typescript-sdk/**" | |
| - "integrations/**" | |
| - "middlewares/**" | |
| # `pnpm run test` is `nx run-many -t test`, which includes @ag-ui/spec — but | |
| # only once this workflow is triggered, so a change confined to the schema, | |
| # a fixture or the harness would otherwise merge without the suite running. | |
| - "spec/**" | |
| # The spec suite's drift gate also compares the generated Python and C# | |
| # files byte for byte, so a hand edit confined to them must trigger it | |
| # too; the proto suite additionally decodes the committed .NET byte | |
| # fixtures. | |
| - "sdks/python/ag_ui/_generated/**" | |
| - "sdks/dotnet/src/AGUI.Abstractions/Generated/**" | |
| - "sdks/dotnet/src/AGUI.Protobuf/Generated/**" | |
| - "sdks/dotnet/tests/AGUI.Protobuf.UnitTests/Fixtures/**" | |
| # The published schema is a generated file under the same drift gate, and | |
| # the docs folder is the one place it can be edited or deleted without | |
| # touching spec/ at all. | |
| - "docs/spec/**" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - "package.json" | |
| - "nx.json" | |
| - ".github/workflows/unit-typescript-sdk.yml" | |
| permissions: | |
| contents: read | |
| jobs: | |
| typescript: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Detect fork PR | |
| id: fork-check | |
| run: | | |
| if [[ "$GITHUB_EVENT_NAME" == "pull_request" && \ | |
| "$PR_HEAD_REPO" != "$GITHUB_REPOSITORY" ]]; then | |
| echo "prefix=fork-" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "prefix=" >> "$GITHUB_OUTPUT" | |
| fi | |
| env: | |
| # Passed via env rather than interpolated into the script: a fork's repo | |
| # name is attacker-controlled text. GITHUB_EVENT_NAME and | |
| # GITHUB_REPOSITORY are Actions built-ins, so nothing is expanded here. | |
| PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version-file: ".node-version" | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 | |
| with: | |
| version: 10.33.4 | |
| - name: Setup pnpm cache | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.local/share/pnpm/store | |
| key: ${{ steps.fork-check.outputs.prefix }}${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ steps.fork-check.outputs.prefix }}${{ runner.os }}-pnpm-store- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Test Build | |
| run: pnpm run build | |
| # A verification build must not rewrite tracked files (PNI-303): the proto | |
| # generator is pinned via config.protocVersion and tsdown's exports | |
| # rewriting is committed in canonical order, so any drift here is a bug. | |
| - name: Verify build leaves the worktree clean | |
| run: git diff --exit-code | |
| - name: Run tests | |
| run: pnpm run test | |
| # @ag-ui/core declares zod ^3.25.76 but all its runtime code uses the | |
| # zod/v4 subpath API, which both supported majors ship. The matrix packs | |
| # the built package and consumes it under zod 3 and zod 4, through | |
| # require() and import, so an API drift on either major fails here. | |
| - name: Verify both zod majors (both module formats) | |
| run: node sdks/typescript/packages/core/scripts/verify-zod-matrix.mjs | |
| # The lane above runs against the SDK version the lockfile pins. The | |
| # aws-strands package declares an open-ended `@strands-agents/sdk` peer, and | |
| # consumers install it prebuilt, so they can be running a newer SDK than the | |
| # one this repository compiles against. | |
| # | |
| # That gap is load-bearing for this integration. The adapter decides what to | |
| # carry onto each per-thread agent from a table checked against | |
| # `keyof AgentConfig`, and that check happens here, at build time, against the | |
| # pinned type. It can say nothing about a field a later release adds, so a new | |
| # field is dropped in silence on exactly the versions the peer range | |
| # advertises as supported. | |
| # | |
| # This lane closes it by resolving the newest published SDK and running the | |
| # package's own suite and typecheck against it. A red build here means the SDK | |
| # gained something the adapter has not been taught. | |
| aws-strands-typescript-latest: | |
| name: aws-strands-typescript-latest | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version-file: ".node-version" | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 | |
| with: | |
| version: 10.33.4 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build workspace dependencies | |
| run: pnpm exec nx build @ag-ui/aws-strands | |
| - name: Install the newest published Strands SDK | |
| working-directory: integrations/aws-strands/typescript | |
| # pnpm, not npm: this is a pnpm workspace and this package depends on | |
| # its siblings through the `workspace:` protocol, which npm refuses | |
| # with EUNSUPPORTEDPROTOCOL. | |
| # | |
| # This rewrites package.json and the lockfile. That is deliberate and | |
| # confined to the runner: the whole point of the lane is to report on a | |
| # version the repository has not adopted, so it does not also assert | |
| # that those files are untouched afterwards. Nothing is committed. | |
| run: | | |
| set -euo pipefail | |
| pnpm add -D "@strands-agents/sdk@latest" | |
| pnpm list @strands-agents/sdk --depth 0 | |
| - name: Typecheck against the newest release | |
| working-directory: integrations/aws-strands/typescript | |
| run: pnpm exec tsc --noEmit | |
| - name: Run tests against the newest release | |
| working-directory: integrations/aws-strands/typescript | |
| run: pnpm exec vitest run |