sync: upstream v4.27.0 (alpha 1)#83
Conversation
Establish the sync branch for upstream Vercel Chat 4.27.0 (release commit f55378a, Apr 30 2026). No feature ports yet — this bumps version to 0.4.27a1, sets UPSTREAM_PARITY = "4.27.0", and lays out the 22-commit port plan in CHANGELOG.md. Each substantive commit will land as its own PR matching the cadence used during the 4.26.0 sync (#64, #66, #67, #74, etc.). The fidelity workflow stays pinned to chat@4.26.0 until the first feature port lands or upstream publishes a chat@4.27.0 tag (only @chat-adapter/shared@4.27.0 was tagged on Apr 30; the chat package version was bumped via package.json only). Local fidelity against chat@4.26.0 still reports 0 missing. Against the new upstream there are 22 missing tests — the expected sync work. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR prepares the chat-sdk-python repository for an alpha release (0.4.27a1) targeting upstream Vercel Chat 4.27.0. Version constants and metadata are bumped, release notes and sync documentation are created, known non-parity items with upstream are documented, and test names are aligned to upstream conventions. ChangesAlpha Release 0.4.27a1 Sync Preparation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Warning Review ran into problems🔥 ProblemsStopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request initiates the sync with upstream version 4.27.0 by bumping the package version to 0.4.27a1 and updating the UPSTREAM_PARITY constant. It includes a detailed porting plan in the changelog and updates documentation in README.md and CLAUDE.md to reflect the new sync status. Feedback was provided to use full repository references for upstream pull requests in the changelog to ensure they link correctly to the vercel/chat repository instead of local PRs.
| - [ ] **`chat.getUser(adapter, userId)`** for cross-platform user lookups (#391, upstream commit `a520797`). Adapter-side: each adapter exposes `getUser`. Touches `chat.py`, `types.py` (`User` type extension), and every adapter (`slack`, `teams`, `gchat`, `telegram`, `discord`, `whatsapp`, `github`, `linear`). | ||
| - [ ] **`ExternalSelect.initial_option` + `option_groups`** (#410, `70281dc`). Type extension in `types.py`; Slack adapter must serialize `option_groups` to Block Kit. | ||
| - [ ] **`thread.post()` streaming options** (#388, `9093292`). New params plumb through `Thread.post` → `chat.py` orchestrator. | ||
| - [ ] **Slack streaming team ID fix for interactive payloads** (#330, `8a0c7b3`). Bug fix in the Slack streaming path; check `adapters/slack/adapter.py` request-context plumbing. | ||
| - [ ] **Bundled guide markdown + templates manifest** (#423, `b0ab804`). Decision: skip or copy `packages/chat/resources/guides/*.md` and `templates.json` verbatim. Probably skip — these are TS-monorepo authoring resources, not runtime behavior. | ||
| - [x] **`concurrency.maxConcurrent` honored in `concurrent` strategy** (#419, `d630e6c`). Already addressed in the Python port — see the existing `ConcurrencyConfig.max_concurrent` row in `docs/UPSTREAM_SYNC.md` (we enforce via `asyncio.Semaphore` and reject misconfiguration). Upstream has now caught up; on this sync the divergence row downgrades from "silent correctness bug upstream" to "behavior parity restored". | ||
|
|
||
| #### Slack (`packages/adapter-slack/` → `src/chat_sdk/adapters/slack/`) | ||
|
|
||
| - [ ] **Slack Socket Mode support** (#162, `7e9d0fc`). Big — adds a persistent WebSocket transport alongside HTTP webhooks. Decision: in scope or follow-up? Mirrors the Discord Gateway gap already documented in non-parity ("HTTP interactions only"). | ||
| - [ ] **Dynamic `bot_token` resolver + custom `webhookVerifier`** (#421, `2531e9c`). Multi-workspace pattern; touches `SlackAdapter.__init__` and request handling. | ||
| - [ ] **External-select Block Kit support** (#397, `a179b29`). Pairs with the core `option_groups` change above. | ||
| - [ ] **Native `markdown_text` for outgoing messages** (#440, post-release — Apr 17). NOTE: this commit is post-`f55378a` so technically out of `4.27.0` scope, but listed here because the team often picks up post-release fixes. | ||
| - [ ] **Link-preview unfurl metadata enrichment** (#395, `ded6f78`). | ||
| - [ ] **`@mention` regex preserves email addresses** (#394, `c26ee6c`). | ||
| - [ ] **Guard against empty `threadTs` (`invalid_thread_ts` fix)** (#292, `53c6b68`). | ||
|
|
||
| #### Teams (`packages/adapter-teams/` → `src/chat_sdk/adapters/teams/`) | ||
|
|
||
| - [ ] **Native streaming for DMs via `emit`** (#416, `ed46bae`). Currently the Python port falls back to `_fallback_stream` for Teams; native streaming would lift that. | ||
| - [ ] **DM conversation ID resolution for Graph API** (#403, `4c24c94`). Bug fix. | ||
| - [ ] **Teams SDK 2.0.8 + `User-Agent` header** (#415, `885a471`). TS-side dependency bump; Python equivalent is to verify our `botbuilder` pin and propagate `User-Agent` if not already. | ||
|
|
||
| #### Telegram | ||
|
|
||
| - [ ] **MarkdownV2 rendering fixes** (#407, `b9a1961`). Pairs with the streaming-chunk safety trim in #446 (post-`f55378a`). | ||
|
|
||
| #### Discord | ||
|
|
||
| - [ ] **Don't duplicate text when posting card messages** (#256, `7e5b447`). Confirm Python port's `discord/cards.py` doesn't have the same bug. |
There was a problem hiding this comment.
The pull request references in the sync scope section (e.g., #391, #410, #388) refer to upstream vercel/chat PRs. Without the repository prefix, GitHub will attempt to link these to pull requests within this repository, which is confusing given that local PR numbers (like #64 in line 10) are also mentioned. Please use the full reference format (e.g., vercel/chat#391) to ensure they link correctly to the upstream source.
There was a problem hiding this comment.
Addressed in commit f88a272 (docs(changelog): qualify upstream PR refs with vercel/chat# prefix), which landed shortly after this review and qualified all 14 upstream PR refs in CHANGELOG.md with the vercel/chat# prefix. The thread is now is_outdated: true against the current CHANGELOG.md (head d6b267f); local PR refs (#64, #74, #82, plus the in-flight #84–#90) remain bare so GitHub still cross-links them within this repo. Marking as resolved.
Generated by Claude Code
Per gemini-code-assist review on PR #83. Without the repo prefix, GitHub auto-links the upstream PR numbers to local PRs in chat-sdk-python, which collides with the local refs (#64, #66, #67, #74, #82) elsewhere in the file. Use vercel/chat#NNN so the upstream refs link correctly. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Final upstream-coverage audit before merging the 7 sync PRs (#84-#90) identified one undocumented N/A item: vercel/chat#415 (Teams SDK 2.0.8 + User-Agent) is a JS-only botbuilder dependency bump. The Python Teams adapter uses raw aiohttp (no botbuilder), so there is no equivalent dependency to bump. The optional User-Agent: Vercel.ChatSDK header on the ~9 outbound aiohttp call sites is a defense-in-depth nice-to-have; deferred as a follow-up rather than landed in this sync. Updates: - CHANGELOG.md: tick all completed items and link them to their PRs (#84, #85, #86, #87, #88, #89, #90, plus already-merged PR #74). Document #415 inline as N/A. - docs/UPSTREAM_SYNC.md non-parity table: add row for Teams User-Agent header divergence so future syncers don't try to "port" the JS bump. Item #6 (concurrency.maxConcurrent) is already implementation-covered in the Python port (existing divergence row at L492). The 4 new TS concurrency tests in chat.test.ts have Python-specific equivalents at test_chat_faithful.py L2969-3055 that don't name-match — leaving as deferred fidelity-baseline polish since the behavior is verified. Verdict from the coverage audit: all 18 substantive ports across PRs #84-#90 are upstream-verified. No commits in chat@4.26.0..f55378a were missed. Ready to start merging. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Final upstream-coverage audit identified 4 chat.test.ts tests in the [concurrency: concurrent] block whose Python equivalents existed but didn't name-match the fidelity script's TS-name conversion. Rename 3 in place and add the 4th, plus document a divergence the new test exposed. Renames (no semantic change): - test_max_concurrent_bounds_in_flight_handlers → test_should_cap_inflight_handlers_at_maxconcurrent_per_thread - test_max_concurrent_zero_or_negative_raises → test_should_throw_when_maxconcurrent_is_less_than_1 - test_max_concurrent_with_non_concurrent_strategy_raises → test_should_warn_when_maxconcurrent_is_set_with_a_nonconcurrent_strategy (Note: TS warns; Python raises — divergence already documented at docs/UPSTREAM_SYNC.md L492. Test name aligns regardless.) New test: test_should_track_slots_per_thread_independently. The implementation surprised me — it currently uses a single global asyncio.Semaphore (src/chat_sdk/chat.py:352), but upstream's acquireConcurrentSlot keys the in-flight counter by threadId. So max_concurrent=2 with 100 threads serializes everything globally on Python (peak 2 across all threads) but allows 200 concurrent on TS (2 per thread). Test marked pytest.mark.skip with a clear reason pointing at the non-parity row, until the implementation is restructured to a dict[thread_id, asyncio.Semaphore] (with cleanup-on-empty to avoid unbounded growth). Tracked as a follow-up. docs/UPSTREAM_SYNC.md: new row in the by-design non-parity table documenting the global-vs-per-thread slot scope divergence with the production-impact framing. Tests: 7 passed + 1 skipped (the per-thread independence test). Fidelity check: chat.test.ts now matches all concurrency entries; the remaining 2 chat.test.ts gaps are getUser tests closed by PR #90. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_chat_faithful.py (1)
2959-2965: ⚡ Quick winMinor documentation reference update.
The comment at line 2961 references
docs/UPSTREAM_SYNC.md L492, but the currentConcurrencyConfig.max_concurrentnon-parity entry inUPSTREAM_SYNC.mdis at line 493 (after the new per-thread slot-scope entry was added above it). Update the comment to point to the correct line.📝 Suggested fix
- # Aligned with upstream `chat.test.ts > should throw when maxConcurrent - # is less than 1`. Python rejects 0 and negatives at construction time - # with `ValueError` rather than silently falling back to unbounded. + # Aligned with upstream `chat.test.ts > should warn when maxConcurrent + # is set with a non-concurrent strategy`. Behavior divergence: TS warns + # and continues; Python raises (see docs/UPSTREAM_SYNC.md L493 — the + # field is only honored under `"concurrent"`, so silently allocating an + # unused semaphore is misleading). The TS test name is preserved so the + # fidelity script picks up the equivalent test.Wait, I see the issue - I misread the code. Let me re-check. Lines 2959-2965 show the function definition and its comment. The comment at 2960-2964 says L492. But looking at the file changes, the new per-thread entry was added at line 493, which would push the existing entry down. Actually, looking more carefully at UPSTREAM_SYNC.md, line 493 starts with "|
ConcurrencyConfig.max_concurrentslot scope" and line 492 has the previous entry about enforcement. So the comment should reference L493 for the slot-scope divergence, not L492 which is about enforcement.Actually wait, let me re-read the comment. The comment at line 2961 says "see docs/UPSTREAM_SYNC.md L492". But the test is about warning when max_concurrent is used with non-concurrent strategy. Looking at UPSTREAM_SYNC.md line 492 in the annotated code:
Line 492:
| ConcurrencyConfig.max_concurrent | Enforced via asyncio.Semaphore in the "concurrent" strategy path...And line 493 starts the new slot scope entry.
So the comment is referencing the enforcement row (L492), not the slot scope row (L493). That seems correct since this test is about enforcement validation, not slot scope.
Let me re-read to be sure... The test function is
test_should_warn_when_maxconcurrent_is_set_with_a_nonconcurrent_strategy. The comment says it checks that Python raises when max_concurrent is paired with queue/debounce/drop strategies. The docs reference would be to the enforcement row that says "rejects any non-None max_concurrent paired with a non-concurrent strategy".Actually, looking at line 492 in the provided UPSTREAM_SYNC.md:
| `ConcurrencyConfig.max_concurrent` | Enforced via `asyncio.Semaphore` in the `"concurrent"` strategy path; rejects non-integer or `<= 0` values, and rejects any non-`None` `max_concurrent` paired with a non-`"concurrent"` strategy |That matches! The test validates rejection of max_concurrent with non-concurrent strategies. The comment reference to L492 is correct.
So actually, no issue here. The comment is accurate.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_chat_faithful.py` around lines 2959 - 2965, The docs line reference is already correct — leave the comment in the test function test_should_warn_when_maxconcurrent_is_set_with_a_nonconcurrent_strategy as-is pointing to docs/UPSTREAM_SYNC.md L492 (it documents enforcement/rejection of max_concurrent with non-"concurrent" strategies); do not change it to L493.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/test_chat_faithful.py`:
- Around line 2959-2965: The docs line reference is already correct — leave the
comment in the test function
test_should_warn_when_maxconcurrent_is_set_with_a_nonconcurrent_strategy as-is
pointing to docs/UPSTREAM_SYNC.md L492 (it documents enforcement/rejection of
max_concurrent with non-"concurrent" strategies); do not change it to L493.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 58440d3d-d821-4fca-84d6-939de70abf85
📒 Files selected for processing (7)
CHANGELOG.mdCLAUDE.mdREADME.mddocs/UPSTREAM_SYNC.mdpyproject.tomlsrc/chat_sdk/__init__.pytests/test_chat_faithful.py
|
@codex review Sync-starter PR for the 4.27.0 wave (version bump to Generated by Claude Code |
|
Codex Review: Didn't find any major issues. Hooray! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Sync starter for upstream
vercel/chat@4.27.0(release commitf55378a, Apr 30 2026). No feature ports in this PR — this is the alpha bump that establishes the sync branch and lays out the porting plan. Each of the 22 substantive commits will land as its own PR, matching the cadence used during the4.26.0sync (#64, #66, #67, #74, …).version→0.4.27a1UPSTREAM_PARITY→"4.27.0"README.md,CLAUDE.md,CHANGELOG.mdwith the in-flight status and full sync planUpstream tagging note
Upstream cut the entire monorepo on Apr 30 2026 (commit
f55378a), bumpingpackages/chat/package.jsonfrom4.26.0to4.27.0. As of this writing only@chat-adapter/shared@4.27.0got a git tag — nochat@4.27.0tag was published. Therefore:.github/workflows/lint.ymlstays pinned to--branch chat@4.26.0for now.scripts/fidelity_baseline.jsonstays pinned tochat@4.26.0. Local devs running fidelity in baseline mode will see a parity mismatch (chat@4.26.0vschat@4.27.0) — that's the intended in-flight signal.chat@4.27.0tag, or when the first feature port lands and we point the clone at commitf55378adirectly.Sync scope
22 substantive upstream commits between
chat@4.26.0..f55378a. SeeCHANGELOG.mdfor the full breakdown by package and per-commit decision (port / already addressed / out of scope).Highlights:
chat.getUser(),ExternalSelect.initialOption + option_groups,thread.post()streaming options, Slack streaming team-ID fix, bundled guides/templates resources.bot_tokenresolver +webhookVerifier, external-select Block Kit, link-preview unfurl enrichment, email-safe@mentionregex, empty-threadTsguard.User-Agent.concurrency.maxConcurrenthonored inconcurrentstrategy — upstream has caught up to our existingasyncio.Semaphoreenforcement.@chat-adapter/web(browser UI; no browser runtime in chat-sdk-python).Test plan
uv run ruff check src/ tests/ scripts/— passesuv run ruff format --check src/ tests/ scripts/— 193 files already formatteduv run python scripts/audit_test_quality.py— 0 hard failures, 39 pre-existing warningsTS_ROOT=/tmp/vercel-chat (chat@4.26.0) uv run python scripts/verify_test_fidelity.py --strict— 0 missing (564/564 matched)uv run pytest tests/— 3,668 pass, 1 pre-existing failure (test_github_webhook.py::TestGitHubAdapterConstructor::test_throws_when_no_auth) unrelated to this PRif: "!github.event.pull_request.draft"); will run when this is marked ready or as feature ports start landing.For the new upstream HEAD, fidelity reports 22 missing tests — that's the expected sync work captured in the plan above.
https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Generated by Claude Code
Summary by CodeRabbit
Documentation
Tests
Chores