Releases: Chinchill-AI/chat-sdk-python
Release list
v0.4.31.1
Python-only fixes on top of 4.31.0 (UPSTREAM_PARITY unchanged at 4.31.0). Two Slack adapter bug fixes, both divergences ahead of upstream, documented in docs/UPSTREAM_SYNC.md.
- Slack #138 — empty-DM
thread_tsfetch now routes toconversations.historyinstead ofconversations.replies(ts='')(infetch_messages/fetch_message), so DM-root context isn't lost. Non-empty (real thread) path is byte-identical. - Slack #95 —
chat.startStreamthreads a workspaceteam_id(= the plumbedrecipient_team_id) for Enterprise Grid orgs, where the per-workspace token alone returnsteam_not_found. Verified end-to-end againstslack_sdkthat the value reaches the API call. - Docs — Linear
"agent-sessions"mode marked experimental (schema-hardened but unverified vs a live tenant, #151).
See CHANGELOG.md.
v0.4.31
Synced to upstream vercel/chat@4.31.0 (UPSTREAM_PARITY = "4.31.0").
Highlights:
- Linear agent-sessions mode (#151, L1–L5): raw-GraphQL, schema-hardened against Linear's published schema (webhook parse/routing, agent-activity emit, fetch, append-only guards)
- Teams SDK-free primitive subpaths:
teams/api,graph,format,webhook,cards/cards_input,modals(each with SSRF/token-leak host gates) - Slack 4.31 (#155): @mention-inside-URL fix,
web_client_options, stable link-buttonaction_id - Telegram rich messages + slash-command router
LinkButton(id=…)stable id, and an opt-in Python-onlyThinkingChunkstream-input type (default-off;StreamChunkunchanged)
The mapped-core test files are byte-identical 4.30→4.31, so the fidelity re-pin to chat@4.31.0 is string-only (732/732, 0 missing). See CHANGELOG.md.
v0.4.30 — Teams SDK migration + 4.30.0 parity
Synced to upstream vercel/chat@4.30.0 (UPSTREAM_PARITY = "4.30.0").
Headline: the Teams adapter is migrated to the official microsoft-teams-apps Python SDK (issue #93 — inbound+auth, outbound, native streaming via the SDK IStreamer, each independently reviewed).
Also in this release:
- New Twilio adapter (10th platform), Telegram native DM draft streaming, Slack webhook/primitives subpaths, and WhatsApp / Slack / Google Chat fixes.
- 7 pre-existing parity gaps closed from a pre-ship parity audit (Google Chat
cardsV2/Select·RadioSelect, Teams ChoiceSet fan-out +list_threads/post_channel_message+api_url,api_urlacross Slack/Discord/GitHub/Linear +get_installation_id, coreAiMessagere-exports). - Documentation accuracy pass correcting earlier phantom-feature / parity-label errors.
CI fidelity re-pinned to chat@4.30.0 (732/732 mapped-core tests). Matches chat@4.30.0 with documented exceptions (Linear agent sessions deferred to 4.31, tracked in #152). See CHANGELOG.md for the full entry.
v0.4.29
Synced to upstream Vercel Chat v4.29.0 (release commit 6581d31; UPSTREAM_PARITY = "4.29.0"). Upstream never tagged chat@4.27.0/chat@4.28.0.
Highlights
- Meta Messenger adapter — the 9th platform
chat/aitool factories (create_chat_tools)callback_urlon buttons and modals (vercel/chat#454)- Transcripts API + per-thread cache rename
message_history→thread_history(vercel/chat#448), with back-compat shims at the old import path and config names message.subject+fetch_subjectadapter hook (#131)- Slack feature wave — verifier precedence flip, external installation providers, native
markdown_text,web_clientproperty (#127), and a DM block-action threading fix (#137) - Teams outbound files via data-URI activity attachments (#125)
- Upstream adapter-hardening security pass (SSRF/URL-allowlist, fail-closed verification)
burstconcurrency strategy
⚠️ Breaking changes
- Python floor raised to 3.12 (was 3.10) — required for the Teams SDK migration line.
- Google Chat fail-closed JWT verification at construction (#130, port of upstream
9824d33):GoogleChatAdapter.__init__now raisesValidationErrorunless one ofgoogle_chat_project_number,pubsub_audience, ordisable_signature_verificationis set (config orGOOGLE_CHAT_*env). Existing gchat deployments must set one of these.
Deferred to 0.4.29.1
- Google Chat multipart file upload (#112) — pending the
/attachments:uploadendpoint + media-scope fixes. - Teams
content.downloadUrlsurfacing (#136) — pending a one-lineis not Noneguideline fix.
See CHANGELOG.md for the complete list of ports and Python-only divergences (docs/UPSTREAM_SYNC.md).
v0.4.27.1 — Slack files_upload_v2 confirmation via SentMessage.raw
Python-only patch on the 0.4.27 line. Does not include the 0.4.29 alpha sync work in flight on main (no upstream 4.29 sync, no Python-floor bump, no auth-precedence change). Branched off the v0.4.27 tag and cherry-picked.
Cut so chinchill-api can gate UX on actual Slack file-delivery confirmation ahead of the 0.4.29 release.
Fixes
- Slack surfaces
files_upload_v2confirmation throughpost().SlackAdapter._upload_filesalready computed the list of Slack-confirmed file IDs, butpost_messagediscarded the return andThreadImpl._create_sent_messagehardcodedraw=None, so the confirmation never reachedSentMessage.raw. Slack was the only file-capable adapter to drop this (discord/telegram upload inline and expose the platform response naturally).post_messagenow augmentsRawMessage.rawwith"uploaded_file_ids"on every return path that can carry files (file-only, card, table, text);ThreadImpl._create_sent_messageaccepts and propagates the adapter'srawintoSentMessage.raw.None= no upload occurred; an empty list = Slack confirmed zero attachments.rawis augmented, not replaced, so existing consumers are unaffected. (Backported from #117; upstream convergence tracked in vercel/chat#564.) chat._MessageHistoryCache.appendnullsrawbefore persisting to history — mirror of the existing null-out inmessage_history.MessageHistoryCache.append. Without it, the platform payload the above fix now populates onSentMessage.raw(Slackteam_id/user_id, etc.) would persist on every reply to Redis/Postgres-backed state, inflating storage and PII surface.
Tests
4 added: three in tests/test_slack_api.py (file-only, text+files, text-only-no-augment paths) and one end-to-end in tests/test_thread_faithful.py verifying post() propagates RawMessage.raw → SentMessage.raw. Full suite green.
Diff vs v0.4.27: +139/-9 across 7 files — strictly additive over the 0.4.27 release.
v0.4.27
Synced to upstream vercel/chat@4.27.0 (release commit f55378a).
Highlights
Upstream parity ports
Chat.get_user(adapter, user_id)across all 8 adapters (#90)- Slack Socket Mode transport (#86) — closes #68
- Slack dynamic
bot_tokenresolver + customwebhook_verifier(#87) - Teams native DM streaming via emit (#88)
- Telegram MarkdownV2 rendering (#89)
- Discord card-text dedup (#89)
- Slack streaming
team_idfix + Teams DM Graph conversation IDs (#85) ExternalSelect.initial_option+option_groups(#84)
Python-only improvements
- Markdown parser: GFM task lists, escaped chars, inline math (#101)
- Streaming markdown: list-marker awareness + table chunk-boundary (#99, issue #69)
- Slack
files_upload_v2kwarg fix (#103, issue #102) - Dict-shaped
StreamChunksupport in slack/github/gchat (#105) - Google Chat card-text markdown rendering (#92)
- Adapter init logs + registered-adapter list in not-found errors (#104)
Sync-process docs
- New "Review-Loop Discipline" section in
docs/UPSTREAM_SYNC.md docs/SELF_REVIEW.mdrewritten with explicit timing + economics
Validation
4,036 tests pass, 3 skipped, 0 failed. ruff/format/audit clean.
Upstream tagging note
Upstream did not publish a chat@4.27.0 tag (only @chat-adapter/shared@4.27.0 got one). The fidelity workflow stays pinned to chat@4.26.0 for this release; will move to a 4.27 SHA pin in the next sync.
See CHANGELOG.md for the full release notes.
v0.4.26.3 — Slack render_postable AST fix
Python-only patch on top of vercel/chat@4.26.0. No upstream version change.
Fixes
SlackFormatConverter.render_postablenow uses the AST path for all markdown inputs (#81). Previously,PostableMarkdownand{"markdown": ...}dict inputs were routed through a private regex helper (_markdown_to_mrkdwn) that truncated URLs containing parentheses and diverged silently from the TS SDK'sfromAst(parseMarkdown(text))behavior. Both branches now callfrom_markdown, which goes through the AST.strandrawbranches are unchanged.
Structural parity
- Deleted
_markdown_to_mrkdwn— a regex-based private method with no call sites after the fix above. The TS SDK has no equivalent; its presence was an undocumented divergence.
Additions
render_postablenow handles card and object-with-ast inputs — added{"card": ...}dict,{"type": "card", ...}CardElementdict,{"ast": ...}dict, and.card/.astattribute branches, plusstr(message)fallback for unrecognized types. Matches the full union ofAdapterPostableMessagevariants.
Test quality
19 new tests in tests/test_slack_format.py (47 → 66) covering all render_postable branches, every _node_to_mrkdwn node type (heading, blockquote, thematic break, image with/without alt), the remaining extract_plain_text paths (strikethrough, bare URL, channel mentions), and to_blocks_with_table edge cases (non-dict AST, standalone table, column alignment).
v0.4.26.2 — upstream parity catch-up
Parity catch-up release with upstream vercel/chat@4.26.0.
Highlights
Thread.get_participants()(#54) — upstream-parity portonOptionsLoad+ Slackblock_suggestion(#50) — external-select dispatch with 2.5s timeoutrehydrateAttachmenthook + SSRF guards (#52) — 5-adapter implementations with per-adapter URL allowlistsStreamingPlan+StreamingPlanOptions(#56) — addskind=='stream'branch inThread.post()(options were previously silently dropped)IoRedisStateAdapter(#71) — cross-runtime Redis interop with TS deployments- Teams cert config shape (#58) — parity with upstream's deprecated-throws stub
- 20
[post with Plan]+ 6[Streaming]test ports (#55, #56) + 3 Plan divergence fixes (UpdateTaskInput.id, error propagation, FIFO edit queue) - Strict fidelity CI (#53) —
verify_test_fidelity.pywired into lint workflow, fails on any missing test or missing upstream checkout
Fidelity
588/588 matched (mapped core files) against chat@4.26.0. Closed [getParticipants], [thread], [Options Load], [post with Plan], [Streaming], and [concurrency: queue attachment rehydration] gaps.
Test coverage
3640 passed, 2 skipped (up from 3545 in 0.4.26.1).
Python-first divergences (documented in docs/UPSTREAM_SYNC.md)
StreamingPlan.is_supported()/get_fallback_text()raise on misroute (upstream silently posts blank)rehydrate_attachmentURL allowlists on Slack / Teams / Google Chat (SSRF defense — upstream doesn't validate)- Redis token format uses CSPRNG hex (upstream uses
Math.random()base36; interop preserved via full-string equality)
Follow-ups tracked
- #78 — extend fidelity MAPPING to all 17
packages/chat/src/*.test.tsfiles - #79 — pin upstream clone SHA in CI
- #80 — tighten fuzzy matcher against hyphen-stripped accidental matches
Full changelog: CHANGELOG.md
v0.4.26.1 — Python-only follow-up on 4.26
0.4.26.1 (2026-04-23)
Python-only follow-up on 0.4.26. Still alpha — APIs may change.
Fixes
- Slack native streaming:
SlackAdapter.stream()no longer calls
AsyncWebClient.chat_stream(...)withoutawait. The unawaited coroutine
returned a truthy object, and the firststreamer.append(...)raised
AttributeError, breaking native Slack streaming for any consumer using
the default adapter. Issue #44. - Teams divider renders at non-zero height: empty
Containerwith
separator: Truerendered as zero-height in the Teams UI. Dividers
between siblings now hoistseparator: Trueonto the following element;
a trailing divider emits a minimal non-empty Container. Issue #45. ConcurrencyConfig.max_concurrentis now enforced: consumers setting
concurrency=ConcurrencyConfig(strategy="concurrent", max_concurrent=N)
now actually get anasyncio.Semaphore(N)cap on in-flight handlers.
Previously the field was accepted and ignored (upstream TS has the same
gap).None/ unset keeps the unbounded default. Issue #51.
Python-specific (divergence from upstream 4.26)
- Fallback streaming runtime robustness (cluster of fixes): framework-
agnosticrequest.text()handling now tolerates sync Flask-style
requests (was raisingTypeError: object is not awaitable). Handlers
typedCallable[..., Awaitable[None] | None]may return sync (None) —
the dispatcher nowawaits only wheninspect.isawaitable()confirms,
preventing runtime crashes on sync handlers. max_concurrentenforcement (see above) — upstream accepts the
config field but never enforces it; we do.
New public APIs
Chat.thread(thread_id, *, current_message=None): new worker-
reconstruction factory mirroring TSchat.thread(threadId). Adapter is
inferred from the thread-ID prefix; state and message history come from
the Chat instance.current_messageis preserved so Slack native
streaming still works post-reconstruction. Issue #46.SlackAdapter.current_token/current_client: public@property
accessors for the request-context-bound bot token and a preconfigured
AsyncWebClient. Replaces underscore access from consumer code making
direct Slack Web API calls inside a handler (email resolution, user
profile fetches, etc.). Issue #47.
Internals
- Pyrefly: 213 → 0 type errors; baseline file removed. CI now enforces
zero errors. Root causes fixed: 8-adapterlock_scope: LockScope | None
protocol conformance;_ChatSingletonasProtocol; submodule-aware
replace-imports-with-any;NoReturnon error re-raisers;
inspect.isawaitableguards for duck-typed request handling and
sync-or-async handler dispatch. NoAnywidening, no new# type: ignorelines beyond 10 at adapter event-construction sites where
thread=None/channel=Noneget re-wrapped byChatbefore handler
dispatch (matches upstream TS'sOmit<>partial-event pattern). - Test count: 3545 passed, 2 skipped.
Known gaps (not fixed in this release)
onOptionsLoadhandler for dynamic select dropdowns — issue #50Thread.getParticipants()method — issue #54rehydrate_attachmentadapter hook for queue/debounce + attachments —
issue #52- 40 upstream tests without Python equivalents (Options Load, Plan variants,
StreamingPlan options, getParticipants) — issue #53 - Discord native Gateway WebSocket (HTTP-only today) — issue #57
- Teams certificate-based mTLS auth — issue #58
- Google Chat file uploads (TODO upstream too) — issue #59
- Global handler-dispatch bound across reactions/actions/slash/modals — issue #61
v0.4.25 — Synced to Vercel Chat 4.25.0
Synced to Vercel Chat 4.25.0. New versioning: 0.{upstream_major}.{upstream_minor} embeds the upstream version directly.
Upgrading
pip install chat-sdk==0.4.25Version scheme changed from 0.0.1aX to 0.{upstream_major}.{upstream_minor}[.patch]. Check chat_sdk.UPSTREAM_PARITY programmatically.
New features (from upstream 4.25.0)
- Plan blocks: Post structured task lists with live updates.
plan = Plan(title="Deploy steps") posted = await thread.post(plan) await plan.add_task("Build image") await plan.update_task(0, status="completed") await plan.complete()
- Streaming table option:
StreamingMarkdownRenderer(wrap_tables_for_append=False)for platforms with native table support. Slack uses this by default. - Teams Select/RadioSelect: Card elements render as Adaptive Card
Input.ChoiceSetwith auto-submit. - GitHub issue threads:
issue_commentwebhooks on plain issues create threads withgithub:owner/repo:issue:42. - Slack OAuth redirect fix:
handle_oauth_callbackcorrectly forwardsredirect_uri.
Python-only improvements (not in upstream)
- PostableObjects cached in message history with real message ID (upstream skips this)
- Teams
msteamstransport key stripped from action values (upstream leaks it) - End-to-end integration tests for Teams card inputs
- GitHub
fetch_threadround-trip test with channel APIs
Internals
- 3,421 tests, 0 warnings, 0 lint errors
- 535/535 TS fidelity (100%)
- Version mapping and sync procedure in UPSTREAM_SYNC.md