Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ebc4c97
feat: add JSON response format support and dynamic model fetching
ttlequals0 Jan 31, 2026
73df648
feat: add debug logging for JSON extraction and enforcement
ttlequals0 Jan 31, 2026
90b92e8
fix: reinforce JSON mode instruction in user prompt
ttlequals0 Jan 31, 2026
cabf0f6
feat: strengthen JSON mode instructions and add debug logging
ttlequals0 Jan 31, 2026
a9c30e5
Merge pull request #1 from ttlequals0/feature/json-format-and-dynamic…
ttlequals0 Feb 1, 2026
5e27ccb
feat: improve JSON extraction reliability and add request cache
ttlequals0 Feb 5, 2026
69d71c3
feat: add dynamic model refresh endpoint
ttlequals0 Feb 7, 2026
7bc615a
feat: add auth method support for model refresh endpoint
ttlequals0 Feb 7, 2026
b592b6a
Merge pull request #2 from ttlequals0/feature/json-improvements
ttlequals0 Feb 7, 2026
4ba088f
feat: update models, tools, pricing, and add retry/cost tracking (v2.…
ttlequals0 Mar 31, 2026
ed0d38d
Merge pull request #3 from ttlequals0/feature/source-code-improvements
ttlequals0 Mar 31, 2026
81502ea
feat: redesign landing page UI and update API docs (v2.5.1)
ttlequals0 Apr 1, 2026
3b7c5bd
Merge pull request #4 from ttlequals0/feature/ui-redesign-api-docs-up…
ttlequals0 Apr 1, 2026
da96713
docs: update README with JSON mode, full endpoint list, fix URLs
ttlequals0 Apr 1, 2026
62e6c74
docs: fix README accuracy issues and tighten language
ttlequals0 Apr 1, 2026
e6b3f3b
docs: add Docker Hub image info to README
ttlequals0 Apr 1, 2026
d80b463
fix: remove fake tools, add 11 real tools from Claude Code source (v2…
ttlequals0 Apr 2, 2026
d2b69a3
Merge pull request #5 from ttlequals0/feature/fix-tool-inventory
ttlequals0 Apr 2, 2026
6dd0acd
feat: function calling, JSON schema, fence stripping, CPU watchdog (v…
ttlequals0 Apr 2, 2026
8c3ba9d
fix: address code review findings
ttlequals0 Apr 2, 2026
750cf9c
chore: remove .hypothesis test cache, add to gitignore
ttlequals0 Apr 2, 2026
7788a13
Merge pull request #6 from ttlequals0/feature/v2.6.0-function-calling…
ttlequals0 Apr 2, 2026
c24dab7
feat: refresh[38;2;1…
ttlequals0 Apr 16, 2026
564aed0
chore: ignore .hypothesis/ test cache at repo root
ttlequals0 Apr 16, 2026
d6dcb26
Merge pull request #7 from ttlequals0/feature/v2.7.0-model-list-refresh
ttlequals0 Apr 16, 2026
fc58143
feat(2.8.0): stop error_max_turns from leaking [Request interrupted b…
ttlequals0 Apr 23, 2026
64f0538
chore: regenerate poetry.lock for exact SDK pin
ttlequals0 Apr 23, 2026
013ffcd
fix: stamp BUILD_INFO inside the poetry venv instead of system python3
ttlequals0 Apr 23, 2026
936c2d8
fix(2.8.1): unmask structured log state, loosen breaker defaults, cap…
ttlequals0 Apr 23, 2026
e821a88
chore(2.8.2): bump deps to clear trivy HIGH/CRITICAL
ttlequals0 Apr 23, 2026
e6e8804
feat(2.9.0): bump claude-agent-sdk 0.1.18 -> 0.1.65
ttlequals0 Apr 23, 2026
d06ed45
Merge pull request #8 from ttlequals0/fix/v2.7.1-max-turns-and-error-…
ttlequals0 Apr 23, 2026
07eac89
fix(2.9.1): close CodeQL alerts for stack-trace exposure and ReDoS
ttlequals0 Apr 24, 2026
f21ff17
chore(2.9.1): harden Docker build and CI, tailor claude.yml for this …
ttlequals0 Apr 24, 2026
36405e6
style: apply black 24 formatting across src/ and tests/
ttlequals0 Apr 24, 2026
7e8f45e
release(2.9.2): version bump to cover docker/CI hardening
ttlequals0 Apr 24, 2026
3370cb9
ci: drop docker smoke-build job
ttlequals0 Apr 24, 2026
fb5dea0
compose: add pull_policy: always on the wrapper service
ttlequals0 Apr 24, 2026
c4d2795
fix(2.9.3): pin claude-agent-sdk[otel] to fix ModuleNotFoundError
ttlequals0 Apr 24, 2026
ddc55d2
Merge pull request #9 from ttlequals0/fix/security-alerts-v2.9.1
ttlequals0 Apr 24, 2026
928f389
docs: audit README against current state, drop stale docs/
ttlequals0 Apr 24, 2026
ee9d9f5
chore: add Dependabot + weekly SDK version sentinel
ttlequals0 Apr 24, 2026
624fde9
Merge pull request #11 from ttlequals0/docs/readme-audit-and-prune
ttlequals0 Apr 24, 2026
85bec5e
Merge pull request #12 from ttlequals0/chore/sdk-version-monitoring
ttlequals0 Apr 24, 2026
955044b
fix(2.9.4): close all seven open Dependabot alerts (#13)
ttlequals0 Apr 24, 2026
3a846ae
ci(sdk-check): write drift to job summary instead of opening an issue…
ttlequals0 Apr 27, 2026
fe32380
chore(deps): bump claude-agent-sdk 0.1.65 -> 0.1.68 (v2.9.5) (#15)
ttlequals0 Apr 27, 2026
41f1d17
fix(v2.9.6): SDK 0.1.81, urllib3/python-multipart sec fixes, dynamic …
ttlequals0 May 11, 2026
eb0db62
fix(v2.9.7): active Claude-CLI auth probe + 401 on cli auth failure
ttlequals0 May 13, 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
53 changes: 53 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Version control
.git
.gitignore
.gitattributes

# Python build / runtime caches
__pycache__
*.py[cod]
*$py.class
*.egg-info
.pytest_cache
.hypothesis
.mypy_cache
.ruff_cache
.coverage
coverage.xml
htmlcov

# Local virtualenvs (Dockerfile creates its own via Poetry)
.venv
venv
env

# Local-only config and secrets
.env
.env.*
!.env.example
*.pem
*.key
*.pfx
id_rsa*
credentials*
secrets*

# Tests / docs / examples are not needed at runtime
tests
docs
examples

# Editor / OS cruft
.DS_Store
.idea
.vscode
*.swp
*~

# CI / local tooling state
.github
.claude

# Planning / local notes
plans
*.tmp
32 changes: 30 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,36 @@ MAX_TIMEOUT=600000
CORS_ORIGINS=["*"]

# Model Configuration
# Default Claude model to use when none specified in request
DEFAULT_MODEL=claude-sonnet-4-5-20250929
# Default Claude model to use when none specified in request.
# When unset AND ANTHROPIC_API_KEY is configured, the wrapper resolves the
# latest Sonnet from Anthropic's live Models API at startup. Otherwise it
# falls back to claude-sonnet-4-6.
# DEFAULT_MODEL=claude-sonnet-4-6

# Speed/cost-optimized model alias.
# FAST_MODEL=claude-haiku-4-5-20251001

# Model Discovery (optional)
# ANTHROPIC_API_KEY unlocks two best-effort enhancements:
# 1. /v1/models returns Anthropic's live model list (cached for 1 hour)
# 2. DEFAULT_MODEL resolves to the latest Sonnet at startup
# It is NOT required to run the wrapper - Bedrock, Vertex, and Claude CLI
# subscription auth all work without it; /v1/models then returns the static
# fallback list.
# ANTHROPIC_API_KEY=sk-ant-...

# Pin the advertised model list. Takes precedence over both live and static.
# CLAUDE_MODELS_OVERRIDE=claude-sonnet-4-6,claude-opus-4-6

# Cache TTL for live /v1/models results (seconds).
# MODEL_LIST_CACHE_TTL_SECONDS=3600

# Short cache TTL when the live fetch fails so transient outages don't
# suppress live discovery for the full hour.
# MODEL_LIST_ERROR_TTL_SECONDS=60

# HTTP timeout for the live model fetch.
# MODEL_LIST_REQUEST_TIMEOUT_SECONDS=5

# Rate Limiting Configuration
RATE_LIMIT_ENABLED=true
Expand Down
49 changes: 49 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
version: 2
updates:
# Python dependencies via Poetry. claude-agent-sdk ships on a fast
# cadence (47 patch releases between 0.1.18 and 0.1.65 in the window
# covered by CHANGELOG 2.9.0); weekly checks keep the drift bounded
# without drowning review in daily PRs.
- package-ecosystem: pip
directory: "/"
schedule:
interval: weekly
day: monday
time: "06:00"
timezone: Etc/UTC
open-pull-requests-limit: 5
labels:
- dependencies
- python
commit-message:
prefix: "chore(deps)"
include: scope
groups:
# Group minor/patch bumps so the review queue stays short.
python-minor-patch:
patterns:
- "*"
update-types:
- minor
- patch
# claude-agent-sdk stays exact-pinned on purpose (see pyproject.toml).
# Do not let Dependabot widen the constraint - it must only propose
# a new exact pin.
ignore: []

# GitHub Actions versions (actions/checkout, codecov/codecov-action,
# snok/install-poetry, etc). Weekly keeps supply-chain drift bounded.
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
day: monday
time: "06:00"
timezone: Etc/UTC
open-pull-requests-limit: 5
labels:
- dependencies
- github-actions
commit-message:
prefix: "chore(ci)"
include: scope
181 changes: 181 additions & 0 deletions .github/workflows/check-sdk-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
name: Check claude-agent-sdk version

# Belt-and-suspenders on top of Dependabot: every Monday, fetch the
# latest claude-agent-sdk release from PyPI and compare to the pin in
# pyproject.toml. If we are behind, open a draft PR with the pin bump
# and regenerated poetry.lock so a human reviewer just adds the version
# bump + CHANGELOG entry before merging. Also runnable manually.
#
# Idempotent: skips PR creation when an open PR for that head branch
# already exists. Job summary fallback runs unconditionally on drift
# so the run page always carries the version delta even if PR creation
# can't run (existing PR, branch conflict, etc.).
#
# Workflow injection notes: schedule + workflow_dispatch are the only
# event sources, so no user-controlled event payload is involved. The
# values flowing into run blocks (pinned, latest, branch) are derived
# from pyproject.toml and pypi.org JSON, and are passed via env: so
# they never reach the shell via ${{ }} expression interpolation.

on:
schedule:
- cron: "0 14 * * 1" # Mondays 14:00 UTC
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- name: Compare pinned SDK vs latest PyPI release
id: compare
run: |
set -euo pipefail

pinned=$(python3 -c '
import re, pathlib
text = pathlib.Path("pyproject.toml").read_text()
m = re.search(r"claude-agent-sdk\s*=\s*(?:\"([^\"]+)\"|\{version\s*=\s*\"([^\"]+)\")", text)
if not m:
raise SystemExit("Could not find claude-agent-sdk pin in pyproject.toml")
print((m.group(1) or m.group(2)).lstrip("^~="))
')

latest=$(curl -sSf https://pypi.org/pypi/claude-agent-sdk/json | python3 -c 'import json,sys; print(json.load(sys.stdin)["info"]["version"])')

echo "pinned=$pinned" >> "$GITHUB_OUTPUT"
echo "latest=$latest" >> "$GITHUB_OUTPUT"

if [ "$pinned" = "$latest" ]; then
echo "up_to_date=true" >> "$GITHUB_OUTPUT"
echo "claude-agent-sdk pin $pinned matches latest PyPI release."
else
echo "up_to_date=false" >> "$GITHUB_OUTPUT"
echo "::warning::claude-agent-sdk pin ($pinned) is behind latest PyPI release ($latest)."
fi

- name: Set up Python
if: steps.compare.outputs.up_to_date == 'false'
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install Poetry
if: steps.compare.outputs.up_to_date == 'false'
run: pipx install poetry==2.3.4

- name: Check for existing bump PR
id: existing
if: steps.compare.outputs.up_to_date == 'false'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LATEST: ${{ steps.compare.outputs.latest }}
run: |
set -euo pipefail
branch="chore/sdk-bump-${LATEST}"
echo "branch=$branch" >> "$GITHUB_OUTPUT"
if [ -n "$(gh pr list --state open --head "$branch" --json number --jq '.[0].number')" ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "An open PR already exists for $branch; skipping create step."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Bump pin, regenerate lock, and open draft PR
if: steps.compare.outputs.up_to_date == 'false' && steps.existing.outputs.exists == 'false'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PINNED: ${{ steps.compare.outputs.pinned }}
LATEST: ${{ steps.compare.outputs.latest }}
BRANCH: ${{ steps.existing.outputs.branch }}
run: |
set -euo pipefail

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"

python3 - <<'PY'
import os, pathlib, re
latest = os.environ["LATEST"]
path = pathlib.Path("pyproject.toml")
text = path.read_text()
# Prefer the table form first (which carries the [otel] extras).
table_pat = re.compile(
r'(claude-agent-sdk\s*=\s*\{[^}]*version\s*=\s*")[^"]+(")'
)
new_text, n = table_pat.subn(r'\g<1>' + latest + r'\g<2>', text, count=1)
if n == 0:
# Fall back to the plain-string form.
string_pat = re.compile(r'(claude-agent-sdk\s*=\s*")[^"]+(")')
new_text, n = string_pat.subn(r'\g<1>' + latest + r'\g<2>', text, count=1)
if n == 0:
raise SystemExit("Failed to update claude-agent-sdk pin in pyproject.toml")
path.write_text(new_text)
PY

poetry lock --no-interaction

git add pyproject.toml poetry.lock
git commit -m "chore(deps): bump claude-agent-sdk $PINNED -> $LATEST"
git push origin "$BRANCH"

gh pr create \
--draft \
--base main \
--head "$BRANCH" \
--title "chore(deps): bump claude-agent-sdk $PINNED -> $LATEST" \
--body "Automated bump opened by the \`Check claude-agent-sdk version\` workflow.

Bumps the SDK pin in \`pyproject.toml\` from \`$PINNED\` to \`$LATEST\` and regenerates \`poetry.lock\`. Scope is deliberately limited to the pin + lock so the human reviewer owns the release coordination.

References:
- Release notes: https://github.com/anthropics/claude-agent-sdk-python/releases/tag/v$LATEST
- Full changelog: https://github.com/anthropics/claude-agent-sdk-python/compare/v$PINNED...v$LATEST
- PyPI: https://pypi.org/project/claude-agent-sdk/$LATEST/

Reviewer checklist before merging:

- [ ] Bump version in \`pyproject.toml\` \`[tool.poetry] version\` and \`src/__init__.py\`
- [ ] Add a new \`## [x.y.z]\` section to \`CHANGELOG.md\` describing this bump
- [ ] Confirm the \`[otel]\` extra is still present on the pin (the SDK unconditionally imports \`opentelemetry.propagate\`)
- [ ] Push an empty commit (\`git commit --allow-empty\`) so the test matrix fires: PRs opened with the default \`GITHUB_TOKEN\` do not trigger downstream \`pull_request\` workflow runs by design
- [ ] Confirm all CI checks pass

Mark the PR ready for review once the items above are in place."

- name: Write drift summary
if: steps.compare.outputs.up_to_date == 'false'
env:
PINNED: ${{ steps.compare.outputs.pinned }}
LATEST: ${{ steps.compare.outputs.latest }}
BRANCH: ${{ steps.existing.outputs.branch }}
PR_EXISTS: ${{ steps.existing.outputs.exists }}
run: |
set -euo pipefail
{
echo "## claude-agent-sdk drift"
echo
echo "| | Version |"
echo "|---|---|"
echo "| Pinned | \`$PINNED\` |"
echo "| Latest on PyPI | \`$LATEST\` |"
echo
if [ "$PR_EXISTS" = "true" ]; then
echo "An open PR for branch \`$BRANCH\` already exists; no new PR was opened."
else
echo "Opened draft PR on branch \`$BRANCH\`."
fi
echo
echo "Release notes: https://github.com/anthropics/claude-agent-sdk-python/releases/tag/v$LATEST"
echo "Full changelog: https://github.com/anthropics/claude-agent-sdk-python/compare/v$PINNED...v$LATEST"
echo "PyPI: https://pypi.org/project/claude-agent-sdk/$LATEST/"
echo
echo "The SDK unconditionally imports \`opentelemetry.propagate\`, so keep the \`[otel]\` extra on the pin."
} >> "$GITHUB_STEP_SUMMARY"
32 changes: 29 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,24 @@ on:
pull_request:
branches: [main]

# Minimum GITHUB_TOKEN scope the jobs need. The Codecov upload reads a
# separate CODECOV_TOKEN from repo secrets; it does not need a broader
# GITHUB_TOKEN scope.
permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
# Hard ceiling so a hung pytest or stalled Poetry install can't chew
# through minutes silently.
timeout-minutes: 15
strategy:
# Don't cancel the rest of the matrix when one Python fails - we want
# to see whether a regression is specific to a single interpreter.
fail-fast: false
matrix:
# pyproject.toml pins python = "^3.10"; test every supported minor.
python-version: ["3.10", "3.11", "3.12"]

steps:
Expand All @@ -34,6 +47,13 @@ jobs:
path: .venv
key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}

# Catch lockfile drift early. The project depends on an exact pin of
# claude-agent-sdk (see CHANGELOG 2.9.0) so pyproject <-> poetry.lock
# disagreement must fail CI rather than silently resolve at install
# time.
- name: Verify lockfile is in sync
run: poetry check --lock

- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root
Expand All @@ -48,11 +68,17 @@ jobs:
run: poetry run mypy src --ignore-missing-imports
continue-on-error: true

- name: Security scan
- name: Static security scan (bandit)
run: poetry run bandit -r src/ -ll -x tests

- name: Dependency vulnerability scan
run: poetry run safety check || true
- name: Dependency vulnerability scan (pip-audit)
# pip-audit replaces the deprecated `safety check` command. It
# reads the installed venv so it matches exactly what will ship.
# Non-blocking: Dependabot / Trivy on the built image are the
# authoritative gates; this is a fast PR signal.
run: |
poetry run pip install --quiet pip-audit
poetry run pip-audit --strict --progress-spinner off || true
continue-on-error: true

- name: Run tests
Expand Down
Loading
Loading