Skip to content

build: move off the yanked claude-agent-sdk 0.1.39 (0.1.39 -> 0.2.157) - #229

Merged
RichardAtCT merged 5 commits into
mainfrom
claude/claude-agent-sdk-0-2-dl3elf
Sep 22, 2026
Merged

RichardAtCT merged 5 commits into
mainfrom
claude/claude-agent-sdk-0-2-dl3elf

Conversation

@claude

@claude claude Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Richard · project thread

Description

Before: poetry.lock pinned claude-agent-sdk to exactly 0.1.39. PyPI has that release yanked, with the reason "This release will be deleted from PyPI on or after 2026-09-19 to free project storage." That date has passed; the files still download, but only until PyPI removes them. Since #224 both ci.yml and release.yml install from the committed lock, so the day the files go, every build and every release fails.

After: the constraint is ^0.2.157 and the lock is regenerated. The lock delta is exactly one package — claude-agent-sdk itself. Its new requirements (jsonschema >=4.20.0, sniffio >=1.0.0, mcp >=1.23.0,<3.0.0) are all already satisfied by packages the lock carries (4.26.0, 1.3.1, 1.26.0), so nothing else moves.

The Python API turned out to be a clean widening. Every symbol src/claude/sdk_integration.py imports still exists in 0.2.157, including the three private ones it depends on (_errors.MessageParseError, _internal.message_parser.parse_message, types.StreamEvent). ClaudeAgentOptions changed two annotations, both widenings: system_prompt gained union members, and effort's inline literal became the EffortLevel alias. The bot passes a plain str for the first and never sets the second.

One real regression did turn up, which is the second commit. CLAUDE_ALLOWED_TOOLS and CLAUDE_DISALLOWED_TOOLS are both Optional, and execute_command passed their value straight through to ClaudeAgentOptions, which declares them as list[str]. 0.1.x tolerated None with a truthiness check; 0.2 does not — the subprocess transport calls list(options.allowed_tools), and a new connect-time shadowing check iterates the same list, so None raises TypeError before the CLI is even started. Both values are now normalised to a list on every path. Behaviour is unchanged: [] and None are both falsy, so the CLI omits the flags either way.

The third commit corrects docs/ROADMAP-v2.md item 0.1, which described this bump and got two things wrong:

  • It called strict skill-name validation (0.2.129) a breaking change to audit. That validation applies to ClaudeAgentOptions.skills, a field 0.2 introduced and this bot never sets. It does not touch allowed_tools.
  • It said to audit CLAUDE_ALLOWED_TOOLS parsing to strip whitespace. src/config/settings.py already does tool.strip() on each entry. The stray space in .env.example was cosmetic, never a bug; it is tidied here anyway.

The fourth commit is a separate concern, added at the maintainer's request rather than found here — see the scope note below. anthropics/claude-code-action refuses a bot-initiated run unless allowed_bots names the account, and it fails the job rather than skipping it, so the review check on this PR died in 20 seconds with Workflow initiated by non-human actor before reading any code. allowed_bots now names claude[bot], and the job condition admits a bot-opened PR only when its branch lives in this repository:

if: >-
  github.event.pull_request.draft == false &&
  (!endsWith(github.event.pull_request.user.login, '[bot]') ||
  (github.event.pull_request.user.login == 'claude[bot]' &&
  github.event.pull_request.head.repo.full_name == github.repository))

Pushing a branch to this repository takes write access, so those commits came from a collaborator or from an app we installed. Commit author fields are deliberately not the check — git commit --author sets them to any name and address, so they prove nothing about who produced a change; who was able to push the branch does. The two gates have to agree, because a bot the condition lets through but allowed_bots does not name would still fail red. Both name claude[bot], so every other bot, and any bot PR from a fork, skips instead of failing. dependabot[bot] is deliberately not admitted yet: its branches are in-repo and would qualify, but GitHub treats secrets differently for Dependabot-triggered runs and that wants checking before claiming it works.

Note this does not fix this PR's own review check. The workflow runs on: pull_request_target, so its definition is always read from the default branch; the change takes effect only once merged.

The fifth commit cuts the changelog for 1.8.0, also at the maintainer's request, since a release follows this merge. Everything that was under [Unreleased] moves into a dated [1.8.0] section with a short note on why minor rather than patch, and a fresh empty [Unreleased] opens above it. pyproject.toml is deliberately left at 1.7.0 — make bump-minor is what moves it, and pre-bumping here would land the release on 1.9.0 instead.

How: the bump itself is pyproject.toml plus poetry lock. The None fix is four lines in execute_command plus a regression test. The roadmap rewrite records what the bump actually was and where the risk actually sits. The workflow change is one input and one condition. The changelog cut is a heading move.

Related issue

Closes #235

The SDK bump itself has no issue — it is docs/ROADMAP-v2.md item 0.1, brought forward because the yank made it urgent.

Type of change

  • Bug fix
  • New feature
  • Breaking change (documented in CHANGELOG under "Changed" or "Removed")
  • Documentation or tooling only

How it was tested

The suite mocks the SDK almost completely — tests/unit/test_claude/test_sdk_integration.py and tests/unit/test_bot/test_stop_button.py are the only files that import it, and both patch heavily — so a green suite shows the types line up, not that the runtime behaves. That matters here because the SDK bundles the Claude Code CLI, which jumps from 2.1.49 to 2.1.277.

Automated, on 0.2.157, with no source changes beyond this PR:

  • black --check src tests — clean, 122 files
  • isort --check-only src tests — clean
  • flake8 src tests — 0 issues
  • pytest — 589 passed (588 before, plus the new regression test)
  • poetry check --lock — passes

mypy is not run: CI does not gate on it and main itself carries pre-existing errors.

By hand, against the real bundled CLI — 7 of 7 passed. Note what this was and was not: I do not have a Telegram bot token in this environment, so there is no live Telegram bot in these results. What I did instead was drive the real ClaudeSDKManager directly against the real bundled CLI with no mocks, which exercises the SDK boundary — the only thing this PR moves. The Telegram transport above it is untouched by this change, but it is also unverified here. Worth a few minutes against a real bot before the release is tagged.

# What Result
1 Resume an existing session Ran a prompt, took the session id off the ResultMessage, resumed with continue_session=True, and the resumed session recalled the codeword from the first turn
2 can_use_tool denial for a path outside APPROVED_DIRECTORY Read on /etc/hostname → PermissionResultDeny("Access denied: path outside approved directory")
3 Bash directory-boundary rejection touch /tmp/smoke-escape.txt → PermissionResultDeny("Directory boundary violation: 'touch' targets '/tmp/smoke-escape.txt' which is outside approved directory ..."), and the file was not created
4 Stop button mid-run interrupt_event set 8s into a long run; the response came back with interrupted=True
5 Streaming partial messages stream_callback received 8 updates across assistant, user and stream_delta — this is the include_partial_messages path that VERBOSE_LEVEL=1 renders, exercised at the SDK boundary rather than through Telegram
6 Interactive approval, Allow INTERACTIVE_TOOL_APPROVAL=true; the callback was prompted for Write and allowed it; the file was written
7 Interactive approval, Deny Same setup, callback returned deny; PermissionResultDeny("Denied by user via Telegram") and no file

The workflow change could not be run. pull_request_target reads the workflow from the default branch, so this change cannot execute until it is merged — that is the same property that makes it unable to fix this PR's own red check. What was checked instead: the file parses as YAML, and the condition was evaluated against every case it has to get right.

PR Outcome
Human author, fork branch reviewed (unchanged — this is most of the repo's traffic)
Human author, in-repo branch reviewed (unchanged)
claude[bot], in-repo branch reviewed (was: red)
claude[bot], fork branch skipped (was: red)
dependabot[bot], in-repo branch skipped (was: red)
Any other bot skipped (was: red)

No case lands on red.

One thing worth recording from the smoke run, because it is directly relevant to #221. With can_use_tool set, 0.2 emits a CanUseToolShadowedWarning at connect time naming every tool the callback will never see. On a default install that is:

can_use_tool will not be invoked for: Glob, Grep, LS, Task, TaskOutput,
WebFetch, TodoRead, TodoWrite, WebSearch, Skill.

That is the SDK detecting by itself the condition #219 reported and #220 fixed by hand. Not acted on here — it is follow-up work, filed separately.

  • Tests added or updated
  • make test and make lint pass locally
  • Tested by hand against a running bot: partially — real bundled CLI, no Telegram bot (see above)

Checklist

  • One concern per PR; unrelated changes are split out
    • Not met, deliberately. This carries three concerns: the SDK bump, the claude-code-review.yml fix, and the 1.8.0 changelog cut. The last two were requested by the maintainer to be carried here rather than split out. Flagging rather than quietly ticking the box.
  • CHANGELOG.md has an entry — now under the dated [1.8.0] heading rather than [Unreleased], since this merge is being released
  • If pyproject.toml dependencies changed, poetry lock was run and the updated poetry.lock is committed
  • Documentation updated (README.md, docs/, .env.example, CLAUDE.md) where settings or commands changed
  • New settings default to current behaviour
  • If AI tools helped write this change, I reviewed every line and the hand-testing above is mine

🤖 Generated with Claude Code

https://claude.ai/code/session_01CH5vbBGwCKYTxkKzwviy8H

PyPI has yanked claude-agent-sdk 0.1.39 with the reason "This release
will be deleted from PyPI on or after 2026-09-19 to free project
storage." That date has passed. The committed lock pins 0.1.39 exactly,
and both ci.yml and release.yml install from the lock, so the day PyPI
removes the files every build breaks.

Bump the constraint to ^0.2.157 and relock. The lock delta is one
package: claude-agent-sdk itself. Its new requirements (jsonschema
>=4.20.0, sniffio >=1.0.0, mcp >=1.23.0,<3.0.0) are all already
satisfied by packages the lock carries, so nothing else moves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH5vbBGwCKYTxkKzwviy8H
CLAUDE_ALLOWED_TOOLS and CLAUDE_DISALLOWED_TOOLS are both Optional, and
execute_command passed their value straight through to
ClaudeAgentOptions, which declares them as list[str].

0.1.x tolerated None with a truthiness check. 0.2 does not: the
subprocess transport calls list(options.allowed_tools), and the new
connect-time shadowing check iterates the same list, so None raises
TypeError before the CLI is even started.

Normalise both to a list on every path. Behaviour is unchanged -- [] and
None are both falsy, so the CLI omits the flags either way -- and the
guarded-tool strip no longer needs its None guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH5vbBGwCKYTxkKzwviy8H
Two claims in ROADMAP-v2.md item 0.1 did not survive contact with
0.2.157:

Strict skill-name validation (0.2.129) is not a breaking change for this
bot. It applies to ClaudeAgentOptions.skills, a field 0.2 introduced and
this bot never sets. It does not touch allowed_tools.

CLAUDE_ALLOWED_TOOLS parsing does not need a whitespace audit.
src/config/settings.py already strips each entry. The stray space in
.env.example was cosmetic, never a bug; tidied here anyway.

Rewrite 0.1 to say what the bump actually was, where the real risk sits
(the bundled CLI, 2.1.49 to 2.1.277, which the mocked suite cannot
cover), and what the live smoke test exercised. Note for #221 that 0.2
emits CanUseToolShadowedWarning at connect naming every tool
can_use_tool will never see. Add the CHANGELOG entries.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH5vbBGwCKYTxkKzwviy8H
@claude

claude Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

CI status: lint and test pass. review fails, and it is not this diff.

The review job died in 20 seconds, before reading any code:

Action failed with error: Workflow initiated by non-human actor: claude
(type: Bot). Add bot to allowed_bots list or use '*' to allow all bots.

That is about who opened the pull request, not what is in it. This PR was opened by the claude[bot] GitHub App, and claude-code-review.yml does not set allowed_bots, so the action refuses to run. Any PR opened by the app fails the same way regardless of its contents. #227/#228 fixed the OIDC failure that used to kill this job; this is a different gate further along in the same action.

I am not fixing it here, for a reason that is structural rather than a judgement call: the workflow runs on: pull_request_target, so its definition is always read from the default branch. A change to claude-code-review.yml on this branch would have no effect on this PR's own run — it only takes effect once merged to main. The workflow's own header comment says as much.

The fix is one input on the anthropics/claude-code-action step, but it is a security decision rather than a typo, so it should be made deliberately and separately. Filed as #235.

Edit: the maintainer asked for the fix to be carried here rather than split out, so it is now in this PR as 830e229, and the body closes #235.

The structural point in the struck-through text still stands, though: the workflow runs on: pull_request_target, so its definition is always read from the default branch. 830e229 therefore does not turn this PR's own review check green — it takes effect only once merged to main. Expect this check to stay red here, on this and any later commit, and to be the last red one. lint and test are green on 830e229.


Generated by Claude Code

anthropics/claude-code-action refuses a bot-initiated run unless
allowed_bots names the account, and it fails the job rather than
skipping it. The review check therefore died in ~20s on #229 with
`Workflow initiated by non-human actor: claude (type: Bot)`, before
reading any code -- the same shape of misleading red check that #227/#228
fixed for `Invalid OIDC token`, at a different gate in the same action.

Name claude[bot] in allowed_bots, and gate provenance in the job
condition: a bot-opened pull request is reviewed only when its branch
lives in this repository. Pushing a branch here takes write access, so
the commits came from a collaborator or from an app we installed.

Commit author fields are deliberately not the check. `git commit
--author` sets them to any name and address, so they prove nothing about
who produced the change; who was able to push the branch does.

The two gates have to agree: a bot the condition lets through but
allowed_bots does not name still fails red. Both name claude[bot], so
every other bot -- and any bot PR from a fork -- skips instead.
Pull requests from human contributors are unchanged, forks included.

Closes #235

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH5vbBGwCKYTxkKzwviy8H
Move everything under [Unreleased] into a dated [1.8.0] section and open
a fresh empty [Unreleased] above it, following the 1.7.0 entry's
convention of a short note on why the version number was chosen.

`make bump-*` only touches pyproject.toml, so this is the step it does
not do. pyproject.toml is deliberately left at 1.7.0: `make bump-minor`
takes it to 1.8.0 when the release is cut.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH5vbBGwCKYTxkKzwviy8H
@RichardAtCT
RichardAtCT merged commit 793c01f into main Sep 22, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The review workflow fails red on any PR opened by a bot (allowed_bots is unset)

2 participants