Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6926d88
fix(shared): update PDX-199 docs error anchors
tylerslaton May 27, 2026
af66c73
Merge branch 'main' into tyler/pdx-199-error-anchor-drift
tylerslaton May 27, 2026
574666a
Merge branch 'main' into tyler/pdx-199-error-anchor-drift
tylerslaton May 27, 2026
55cc955
Merge branch 'main' into tyler/pdx-199-error-anchor-drift
tylerslaton May 27, 2026
31fae47
Merge branch 'main' into tyler/pdx-199-error-anchor-drift
tylerslaton May 27, 2026
ff7e921
Merge branch 'main' into tyler/pdx-199-error-anchor-drift
tylerslaton May 28, 2026
ec239b1
Add v1 reference selector and content
tylerslaton May 28, 2026
70cb273
style: auto-fix formatting
github-actions[bot] May 28, 2026
3e6fc5c
fix(sdk-python): walk _client chain and install async hook on AsyncCl…
jpr5 May 28, 2026
6b6e08f
chore(sdk-python): release v0.1.92
jpr5 May 28, 2026
3110cf9
docs(sdk-python): document header_propagation module purpose and scope
jpr5 May 28, 2026
c6f4cea
docs(landing): overhaul landing page
tylerslaton May 28, 2026
e56634e
chore(showcase): bump copilotkit SDK pin to 0.1.92
jpr5 May 28, 2026
7908788
chore(showcase): update validate-pins fail baseline for copilotkit 0.…
jpr5 May 28, 2026
b552a62
docs(landing): overhaul landing page (#5091)
tylerslaton May 28, 2026
64100d8
Merge branch 'main' into tyler/docs-add-v1-reference-selector
tylerslaton May 28, 2026
e5ed150
fix: add v1 reference version selector for shell-docs (#5086)
tylerslaton May 28, 2026
227365a
ci(sdk-python): add testable PyPI version-change detection script
jpr5 May 28, 2026
65918d0
ci(sdk-python): publish to PyPI via OIDC trusted publishing
jpr5 May 28, 2026
fcc5f53
ci(sdk-python): fail-loud pyproject-change gate + publish guard + doc…
jpr5 May 28, 2026
8b758ac
ci(sdk-python): harden py-version detection — max-over-releases + num…
jpr5 May 28, 2026
f3365d8
ci(sdk-python): zero-pad version compare, drop yanked releases, fail …
jpr5 May 28, 2026
75ab40d
ci(sdk-python): honor dry-run, fail-loud tag push, env-pass token, gu…
jpr5 May 28, 2026
c8fa0bb
Merge branch 'main' into tyler/pdx-199-error-anchor-drift
tylerslaton May 29, 2026
238b12b
docs: fix shared docs error anchors (#5061)
tylerslaton May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
# skips tag push + GH Release + Notion notification.
name: release / publish

# This workflow handles two independent release lanes:
#
# 1. npm (TypeScript) — fires on merged release/publish/* PRs or manual dispatch.
# Build → publish via nx release + OIDC trusted publishers.
#
# 2. PyPI (Python SDK) — fires on any merged PR that bumps sdk-python/pyproject.toml.
# Detects version change vs PyPI registry, builds with poetry, publishes with uv.
# Ported from ag-ui's publish-release.yml Python lane.

on:
pull_request:
types: [closed]
Expand Down Expand Up @@ -48,6 +57,11 @@ on:
required: false
default: false
type: boolean
python_publish:
description: "Run the Python publish lane regardless of which files changed. Still no-ops if sdk-python's version already matches PyPI."
required: false
default: false
type: boolean

permissions:
contents: read
Expand Down Expand Up @@ -371,3 +385,238 @@ jobs:
echo "**Mode:** ${{ steps.meta.outputs.mode }}"
echo "- Publish step was skipped; no npm publish, no git tag, no GitHub Release."
} >> "$GITHUB_STEP_SUMMARY"

# ===========================================================================
# Python SDK publish lane
#
# Fires independently of the npm lane. Detects whether sdk-python/pyproject.toml
# has a version newer than what's on PyPI, builds with poetry, publishes with uv.
#
# SECURITY: Same build/publish separation as the npm lane — PYPI_API_TOKEN is
# only available in the publish-python job, never where poetry install runs.
# ===========================================================================

build-python:
# Fires when:
# 1. A PR merging to main touched sdk-python/pyproject.toml (version bump), OR
# 2. Manual dispatch with python_publish=true
if: >
(github.event_name == 'workflow_dispatch' && inputs.python_publish == true) ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true)
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
outputs:
should_publish: ${{ steps.detect.outputs.should_publish }}
version: ${{ steps.detect.outputs.version }}
name: ${{ steps.detect.outputs.name }}
steps:
- name: Checkout merged main
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
ref: main
persist-credentials: false

# For PRs, skip early if this PR didn't touch pyproject.toml. Manual
# dispatch always continues (the user explicitly asked for it).
- name: Check if pyproject.toml changed in this PR
if: github.event_name == 'pull_request'
id: changed
env:
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.merge_commit_sha }}
run: |
set -euo pipefail
if [ -z "$PR_BASE_SHA" ]; then
echo "::error::PR_BASE_SHA is empty — cannot determine PR base for diff. Refusing to silently skip Python publish."
exit 1
fi
if [ -z "$PR_HEAD_SHA" ]; then
echo "::error::PR_HEAD_SHA (merge_commit_sha) is empty — GitHub may not have computed the merge commit yet. Refusing to silently skip Python publish; rerun the workflow."
exit 1
fi
# Capture diff FIRST so a git failure trips set -e and fails loudly,
# rather than producing an empty pipe that grep silently routes to
# "not changed" — that path masked real version bumps before.
CHANGED="$(git diff --name-only "$PR_BASE_SHA" "$PR_HEAD_SHA")"
# grep -q exits 1 on legitimate no-match; guard with `if` so set -e
# doesn't kill the step on that expected case.
if printf '%s\n' "$CHANGED" | grep -q '^sdk-python/pyproject.toml$'; then
echo "pyproject_changed=true" >> "$GITHUB_OUTPUT"
else
echo "pyproject_changed=false" >> "$GITHUB_OUTPUT"
echo "sdk-python/pyproject.toml not changed in this PR — skipping Python publish"
fi

- name: Set up Python
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.pyproject_changed == 'true'
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"

- name: Detect version change
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.pyproject_changed == 'true'
id: detect
run: ./scripts/release/detect-py-version-changes.sh

- name: Install Poetry
if: steps.detect.outputs.should_publish == 'true'
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true

- name: Build Python package
if: steps.detect.outputs.should_publish == 'true'
working-directory: sdk-python
run: poetry build

- name: Upload Python build artifacts
if: steps.detect.outputs.should_publish == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: py-build-artifacts
path: sdk-python/dist/
retention-days: 1

- name: Nothing to publish
if: steps.detect.outputs.should_publish != 'true'
run: |
{
echo "## Python SDK"
echo ""
echo "No version change detected — nothing to publish."
} >> "$GITHUB_STEP_SUMMARY"

# WARNING: PyPI trusted-publisher binding pins to:
# repository: CopilotKit/CopilotKit
# workflow_file: publish-release.yml
# environment: pypi
# Renaming this file, changing this job's `environment:` value, or moving the
# publish step into another workflow breaks PyPI publishing with HTTP 422
# until the Trusted Publisher record on pypi.org is updated to match.
publish-python:
needs: build-python
if: ${{ !cancelled() && needs.build-python.result == 'success' && needs.build-python.outputs.should_publish == 'true' && inputs.dry-run != true }}
runs-on: ubuntu-latest
timeout-minutes: 10
environment: pypi
permissions:
contents: write
id-token: write
steps:
- name: Checkout merged main
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
ref: main
persist-credentials: false

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: ">=0.8.0"

- name: Download Python build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: py-build-artifacts
path: sdk-python/dist

- name: Publish to PyPI (OIDC trusted publishing)
run: |
set -euo pipefail
shopt -s nullglob
files=(sdk-python/dist/*)
if [ ${#files[@]} -eq 0 ]; then
echo "::error::no build artifacts in sdk-python/dist — nothing to publish"
exit 1
fi
uv publish --trusted-publishing always "${files[@]}"

- name: Verify version is live on PyPI
env:
NAME: ${{ needs.build-python.outputs.name }}
VERSION: ${{ needs.build-python.outputs.version }}
run: |
set -euo pipefail
for i in $(seq 1 18); do
if curl -fsS "https://pypi.org/pypi/${NAME}/${VERSION}/json" >/dev/null 2>&1; then
echo "Confirmed ${NAME}==${VERSION} on PyPI"; exit 0
fi
echo "Attempt ${i}: ${NAME}==${VERSION} not visible yet; retrying in 10s..."
sleep 10
done
echo "::error::${NAME}==${VERSION} did not appear on PyPI within 180s"
echo "Last curl response:"
curl -sS "https://pypi.org/pypi/${NAME}/${VERSION}/json" 2>&1 | tail -n 5 || true
exit 1

- name: Configure git
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git config --local url."https://x-access-token:${GH_TOKEN}@github.com/".insteadOf "https://github.com/"

- name: Create and push git tag
id: tag
env:
PY_VERSION: ${{ needs.build-python.outputs.version }}
PY_NAME: ${{ needs.build-python.outputs.name }}
run: |
set -euo pipefail
TAG="python-sdk/v${PY_VERSION}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists — skipping"
else
git tag -a "$TAG" -m "Release ${PY_NAME} ${PY_VERSION}"
git push origin "$TAG"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Create GitHub Release
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
RELEASE_TAG: ${{ steps.tag.outputs.tag }}
RELEASE_VERSION: ${{ needs.build-python.outputs.version }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
const tag = process.env.RELEASE_TAG;
const version = process.env.RELEASE_VERSION;
const name = `python-sdk/v${version}`;
const body = `Python SDK release: copilotkit ${version}\n\nhttps://pypi.org/project/copilotkit/${version}/`;

try {
const existing = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
await github.rest.repos.updateRelease({
owner, repo,
release_id: existing.data.id,
tag_name: tag, name, body,
draft: false, prerelease: false,
});
} catch (error) {
if (error.status !== 404) throw error;
await github.rest.repos.createRelease({
owner, repo,
tag_name: tag, name, body,
draft: false, prerelease: false,
});
}

- name: Release summary
env:
PY_VERSION: ${{ needs.build-python.outputs.version }}
run: |
{
echo "## Python SDK Published"
echo ""
echo "- \`copilotkit@${PY_VERSION}\`"
echo "- https://pypi.org/project/copilotkit/${PY_VERSION}/"
} >> "$GITHUB_STEP_SUMMARY"
12 changes: 6 additions & 6 deletions packages/shared/src/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,37 +63,37 @@ const getSeeMoreMarkdown = (link: string) => `See more: [${link}](${link})`;
export const ERROR_CONFIG = {
[CopilotKitErrorCode.NETWORK_ERROR]: {
statusCode: 503,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#i-am-getting-a-network-errors--api-not-found`,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#network-errors-api-not-found`,
visibility: ErrorVisibility.BANNER,
severity: Severity.CRITICAL,
},
[CopilotKitErrorCode.NOT_FOUND]: {
statusCode: 404,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#i-am-getting-a-network-errors--api-not-found`,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#network-errors-api-not-found`,
visibility: ErrorVisibility.BANNER,
severity: Severity.CRITICAL,
},
[CopilotKitErrorCode.AGENT_NOT_FOUND]: {
statusCode: 500,
troubleshootingUrl: `${BASE_URL}/coagents/troubleshooting/common-issues#i-am-getting-agent-not-found-error`,
troubleshootingUrl: `${BASE_URL}/langgraph-python/coagent-troubleshooting/common-coagent-issues#i-am-getting-agent-not-found-error`,
visibility: ErrorVisibility.BANNER,
severity: Severity.CRITICAL,
},
[CopilotKitErrorCode.API_NOT_FOUND]: {
statusCode: 404,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#i-am-getting-a-network-errors--api-not-found`,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#network-errors-api-not-found`,
visibility: ErrorVisibility.BANNER,
severity: Severity.CRITICAL,
},
[CopilotKitErrorCode.REMOTE_ENDPOINT_NOT_FOUND]: {
statusCode: 404,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#i-am-getting-copilotkits-remote-endpoint-not-found-error`,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#remote-endpoint-not-found-error`,
visibility: ErrorVisibility.BANNER,
severity: Severity.CRITICAL,
},
[CopilotKitErrorCode.AUTHENTICATION_ERROR]: {
statusCode: 401,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues#authentication-errors`,
troubleshootingUrl: `${BASE_URL}/troubleshooting/common-issues`,
visibility: ErrorVisibility.BANNER,
severity: Severity.CRITICAL,
},
Expand Down
Loading
Loading