Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 36 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,42 @@
# Changelog

## Unreleased (4.41 wave)
## 0.4.41

Sync wave from `chat@4.31.0` to `chat@4.41.1` (tracking #184). `UPSTREAM_PARITY` stays `4.31.0` until #203.
Synced to upstream `vercel/chat@4.41.1` (the 4.41 wave, tracking #184): twelve upstream releases (`chat@4.32.0` → `chat@4.41.1`) in one jump from `chat@4.31.0`. Sets `UPSTREAM_PARITY = "4.41.1"` (the exact strict fidelity pin tag, not `4.41.0`; the package version encodes only major.minor). The strict fidelity pin moves to `chat@4.41.1` (`f690af3c`): **922/922** mapped tests at the pin, up from 733/733 at `chat@4.31.0`, after the eight test files upstream added in 4.34–4.41 (`history/*`, `agent-session`, `app-context`, `installation-events`, `adapter-shared/download`) joined the strict tier. `cards`, `modals`, `emoji` and `message` stay report-only (100 of 142 upstream titles untranslated; issue #78). Upstream changes the wave skips (TanStack AI, AI SDK v7 typing, Workflow `requestApproval`, the serialization entrypoint, Vercel Connect, `create-chat-sdk`, catalog/docs-only commits) and the demand-gated platforms (Instagram, Notion, X, XChat, Telegram Business, Twilio RCS, Gmail #241, Discord Components V2, per-adapter Vercel Connect; #189) are recorded in `docs/UPSTREAM_SYNC.md` § Known Non-Parity.

### Consumer-visible changes (migration checklist)

Each item names the wave issues; their entries under "Wave PRs" below have the details and migration notes.

- **Concurrency (#190, #191).** Thread/channel locks now heartbeat while a handler runs (`max_lock_lifetime_ms`, default 10 min). With the default `drop` strategy, a second message arriving 30 s or more into a long handler (e.g. a long Slack/Teams stream) now raises `LockError` instead of running concurrently; pick `queue`/`debounce`/`burst` or set `on_lock_conflict` to handle it. Dedupe TTL is 10 minutes. `wait_until` no longer surfaces handler errors unless `WebhookOptions(propagate_handler_errors=True)`. A failed adapter `initialize()` is cached until `shutdown()`.
- **Mentions and the message model (#192, #193, #209, #283).** `Message.is_mention` is tri-state: custom adapters must report `None` (not `False`) for "not detected". Emails, URL userinfo and `@name-suffix` no longer mention the bot. `ast_to_plain_text` keeps structural whitespace. On Slack, `message.text` is now the plain text of `message.formatted` (no mrkdwn markup), the bot's own mention reads `@BotName`, special mentions read `@here`/`@eng`, code blocks keep their first line, and bot authors use the `U…` user id. New `Author.email` / `Author.is_system` and `Message.reply_to`.
- **Streaming (#199, #207, #208, #219, #226, #206).** Slack: `native_streaming`, a post+edit fallback (top-level DMs now stream by editing a placeholder), `stream()` returns `None` without recipient context instead of raising, long native streams continue in a new message before Slack's ~5 min expiry (`stream_segment_max_age_ms`), and streamed `@name`s become real pings. Teams (#219): only with an explicit `fallback_streaming_placeholder_text`, DMs show it as the native informative status line and group chats/channels stream by post+edit; otherwise unchanged. Telegram: post-and-edit by default, native drafts opt-in. Core: `fallback_streaming_placeholder_text` defaults to `UNSET`; handle/DM threads have no stub `current_message`.
- **Callback-URL tokens (#194, security).** Single-use, bound to their button and conversation, 7-day TTL. Tokens minted before the upgrade stop resolving.
- **AI tools (#195, #198, security).** Agent tools are confined to the conversation being handled (`scope=False` for workspace-wide access); `getUser` needs approval by default; link metadata is fenced as untrusted. `to_ai_messages` keeps attachment/link-only messages; `ChatTool.name`.
- **Download caps (#204 and adopters #213, #218, #225, #229, #234, #235, #239).** Attachment downloads go through the shared guarded downloader: HTTPS only, internal addresses refused (including after redirects), **25 MB** decoded cap, **30 s** deadline, failures raise `NetworkError`. Slack file downloads no longer honor `HTTPS_PROXY`/`HTTP_PROXY` (set `file_transport`). Messenger, WhatsApp and Twilio narrow their media host/origin allowlists.
- **Webhook authentication (security).** Telegram requires a webhook secret by default (#224; `allow_unverified_webhooks` escape hatch). Google Chat JWT verification is bound to configured identities; Pub/Sub and Workspace Add-on deployments must set the new service-account emails, and `bot_user_id` should be set (#222, #223). Slack caches are installation-scoped and Enterprise Grid org-wide installs work (#205, #268). Teams gains a custom token factory and `webhook_verifier` (#221).
- **Thread and channel ids.** Linear agent sessions use one stable thread `linear:{issue}:s:{session}` (#232; re-subscribe old ids). Twilio `channel_id` equals the thread id (#235). Teams group chats carry a conversation-type segment and are no longer DMs; `TeamsAdapter.bot_user_id` is `28:{app_id}` (#216, #217). Slack `channel.post()` returns a replyable `slack:C…:<ts>` (#283). Discord validates a thread's parent channel, and a thread interaction without `parent_id` encodes as `discord:{guild}:{thread}` (was `discord:{guild}:{thread}:{thread}`) (#229). WhatsApp `Author.user_id` and the thread's user segment may be a BSUID (e.g. `US.1349…`) for users without a shared phone number, and WhatsApp writes `whatsapp:identity:alias:*` / `whatsapp:identity:route:*` state keys (#236).
- **State and dispatch latency.** Postgres `set_if_not_exists` now reclaims an expired row, so Postgres-backed dedupe keys and leases recover without cleanup (#240). Teams looks up the sender's email before dispatch: on a cache miss that is one awaited network call, bounded at 5 s, cached for 1 h (#218).
- **New lifecycle and API surface (additive).** `on_message_updated` / `on_message_deleted`, `on_installed` / `on_uninstalled`, app-context and agent-session events (#196, #201, #211, #215, #217); `chat.history` (#197); `Thread.reply` / `Thread.mark_as_read` (#200); turn cancellation (`chat.abort_turn`, `thread.signal`, #201); Slack `agent_view` and Agent Sessions (#214, #215); cards `Chart`, table options, tooltips, `DateInput` / `NumberInput` (#202, #212, #220); Telegram albums and media groups (#227, #278); WhatsApp templates, media and read receipts (#237, #238, #239).
- **Dependencies.** The `[teams]` extra allows `microsoft-teams-{apps,api,cards,common}` `>=2.0.13,<2.2` (#250).

### Pending live checks before publishing

The maintainer runs these against a live workspace before tagging and publishing 0.4.41 (release gate in #184). This PR does not tag, release or publish.

- [ ] **Teams on SDK 2.1** (#262 / #250): native DM streaming, `edit_message` and `delete_message` on `microsoft-teams-apps` 2.1.x.
- [ ] **Slack streamed mentions** (#206): a streamed `@name` for a cached user becomes a real `<@U…>` ping on the native stream.
- [ ] **Slack stream rotation past 5 minutes** (#208): a native stream longer than `stream_segment_max_age_ms` continues in a new message without losing text.
- [ ] **Slack `agent_view` Stop button** (#215): clicking Stop aborts the running turn and sets the session `active`.
- [ ] Pre-publish git-ref smoke test of downstream consumers (chinchill) against the release commit, as for 0.4.30/0.4.31.

### Wave PRs

- **Release: fidelity pin, `UPSTREAM_PARITY` and version to 0.4.41 / chat@4.41.1** (#203). Tooling and docs only; no runtime change.
- `scripts/upstream_pin.json` `pin` → `chat@4.41.1` @ `f690af3c26a3bd7649387cccad12d215463ed22c` (same as `target`). CI's strict fidelity step clones that tag and checks the SHA. `UPSTREAM_PARITY = "4.41.1"`, `pyproject.toml` version `0.4.41`, README status line, and both version-mapping tables (CLAUDE.md, `docs/UPSTREAM_SYNC.md`).
- Strict tier: the eight test files upstream added in 4.34–4.41, all at 0 missing, move from `TARGET_MAPPING` to `MAPPING` (922/922 at the pin). `fidelity_baseline.json` is regenerated for `ts_parity: chat@4.41.1` (still empty). `cards` / `modals` / `emoji` / `message` stay report-only in `TARGET_MAPPING` (#78).
- `docs/UPSTREAM_SYNC.md`: new "Skipped upstream changes (4.41 wave)" and "Deferred platforms and adapter modes (#189)" tables; the `chat/adapters` catalog row is re-verified at `chat@4.41.1` (34 entries; Dial, Cloudflare Agents, Photon, Linq, Novu, Gmail, Instagram, Notion, X and XChat added since 4.31.0).
- `tests/test_version_mapping_docs.py` keeps the CLAUDE.md and `docs/UPSTREAM_SYNC.md` version-mapping tables in step (same releases, same upstream versions) and ties the `pyproject.toml` version to `UPSTREAM_PARITY`.

- **Slack inbound mrkdwn normalization, `channel.post()` thread ids, Socket Mode retries** (#283, part (b) of #209; ports vercel/chat `0b63791b` #667 (Slack half), `92530dd3` #720, `c3118279` #756, `e71bfead` #843 (Slack half) and `44423bdc` #960, chat@4.33.0–4.41.1). A live Slack-loop check is pending.
- **Breaking/consumer-visible (`message.text` is the plain text of `message.formatted`):** both parse paths now set `text = ast_to_plain_text(formatted)` (upstream `toPlainText(formatted)`), replacing the regex pass over the mrkdwn. Markup no longer leaks into `text`: inline-code backticks, list markers (`- a` → `a`, `1. one` → `one`), heading `#` and quote `>` prefixes are dropped, a trailing newline or a whitespace-only body is trimmed, and blocks are separated by a blank line. `SlackFormatConverter.extract_plain_text` drops its Python-only regex override for the base `to_ast` + `ast_to_plain_text` (upstream has none). *Migration:* match structure on `message.formatted`, or on `message.raw["text"]` for the original mrkdwn.
Expand Down
9 changes: 6 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Claude Code Quick Reference -- chat-sdk-python

## What is this?
Python port of [Vercel Chat SDK](https://github.com/vercel/chat) (synced to upstream v4.31.0). Multi-platform async chat framework.
Python port of [Vercel Chat SDK](https://github.com/vercel/chat) (synced to upstream v4.41.1). Multi-platform async chat framework.

## Key Commands
```bash
Expand Down Expand Up @@ -32,6 +32,7 @@ Our version embeds the upstream Vercel Chat version: `0.{upstream_major}.{upstre
- `0.4.31.1` = Python-only fixes on top of `4.31.0` (Slack #138/#95)
- `0.4.31.2` = tagged, never published (publish action rejected Metadata-Version 2.5; tag immutable) — superseded by
- `0.4.31.3` = Python-only fixes on top of `4.31.0` (Teams Graph SSRF-guard hardening #178, NOTICE); same content as 0.4.31.2
- `0.4.41` = synced to upstream `4.41.1` (the 4.41 wave, #184; `UPSTREAM_PARITY = "4.41.1"`, the exact strict pin tag, not `4.41.0`)
- `UPSTREAM_PARITY` constant in `__init__.py` = programmatic access

## Architecture
Expand Down Expand Up @@ -115,13 +116,15 @@ will not pass CI.
**Fidelity check** (`scripts/verify_test_fidelity.py`) verifies every TS
`it("...")` / `test("...")` in the mapped core files — plus one logical
test per `it.each` template — has a matching Python `def test_*()`,
pinned to `chat@4.31.0` (major.minor must match `UPSTREAM_PARITY`).
pinned to `chat@4.41.1` (major.minor must match `UPSTREAM_PARITY`).
The pin lives in **`scripts/upstream_pin.json`** (`pin` = strict CI tag +
commit SHA, `target` = the sync wave's tag); nothing else hard-codes it.
Scope is two-tier: `MAPPING` is the strict set; `TARGET_MAPPING` rows are
only checked by `--report-target`; `UNMAPPED` lists deliberate skips with
reasons. Every `packages/chat/src/**/*.test.ts(x)` must be in one of the
three (remaining unmapped work: issue #78).
three. At the `chat@4.41.1` pin the strict tier is 922/922; the four
report-only `TARGET_MAPPING` rows (`cards`, `modals`, `emoji`, `message`)
and the `#78 pending` `UNMAPPED` rows are the remaining work (issue #78).
**CI runs `--strict`** (see `.github/workflows/lint.yml`):
any missing translation in a mapped file fails the build, a missing
upstream checkout fails, and a checkout whose HEAD differs from the pinned
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

Multi-platform async chat SDK for Python. Port of [Vercel Chat](https://github.com/vercel/chat) (MIT, © Vercel, Inc. — see [NOTICE](NOTICE)).

> **Status: 0.4.31.3 — synced to [Vercel Chat 4.31.0](https://github.com/vercel/chat)** (`UPSTREAM_PARITY = "4.31.0"`). See [CHANGELOG.md](CHANGELOG.md).
> **Status: 0.4.41 — synced to [Vercel Chat 4.41.1](https://github.com/vercel/chat)** (`UPSTREAM_PARITY = "4.41.1"`). See [CHANGELOG.md](CHANGELOG.md).

## Why chat-sdk?

Expand Down
Loading
Loading