Add capability-gated surface methods across the bot packages so one handler
degrades gracefully per platform:
- thread.react / thread.unreact — emoji reactions (Slack, Discord, Telegram)
- thread.postEphemeral — only-you messages with a required DM fallback
- openModal (interaction/command-scoped) — structured forms (Slack rich,
Discord text-only), omitted on surfaces with no live interaction trigger
Spans @copilotkit/bot-ui, bot, bot-slack, bot-discord, bot-telegram with
per-adapter capability flags. Discord: open modals from slash-command
interactions, and never clear registered commands on an empty publish.
Adds a durable persistence layer for @copilotkit/bot, replacing the
in-memory-only ActionStore with a pluggable StateStore.
- StateStore interface (kv/list/lock/dedup/queue) with a shared
conformance suite; MemoryStore default plus @copilotkit/bot-store-redis
and @copilotkit/bot-store-postgres backends.
- createBot({ store }): typed per-thread state via Standard Schema,
action snapshots persisted through the store, per-conversation turn
lock (onLockConflict drop|force), and inbound-event dedup keyed on a
stable eventId. ActionStore is kept as a deprecated alias.
- Cross-platform transcripts (bot.transcripts + identity resolver) with
age-bounded retention (prune on append + filter on read), and
runAgent({ transcript: true }) to auto-inject history and capture the
reply.
- createBot({ components }) re-registers components so durable actions
re-fire after a restart; restart-durability demo in examples/slack.
- Dedup is marked seen only after the turn lock is acquired, so a turn
dropped on lock-conflict does not burn its eventId (no lost retries).
- Release lockstep: bot-store-redis/postgres version with bot + bot-ui.
## Release bot v0.0.3
**Scope:** `bot` | **Bump:** `patch`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `bot` packages to `0.0.3`
and generated AI-enhanced release notes.
2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
must pass before merging. This is the review gate.
3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.
4. **When this PR is merged**, the `release / publish` workflow
automatically:
- Builds all packages
- Publishes the `bot` packages to npm at version `0.0.3`
- Creates git tag `bot/v0.0.3`
- Creates a GitHub Release with the final release notes
### Before merging
- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)
---
> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
Resolve event-renderer.ts onRunFinishedEvent: keep the native turn stream open
(finalized in finish()) AND retain the legacy per-message stream drain from main
(#5573) as a no-op-in-native safety net. app/index.ts (telegram adapter from
#5520 + showToolStatus:false) and create-bot.test.ts auto-merged.
awaitChoice resolved the waiter with evt.value, which platforms whose callback payload cannot carry it (Telegram, 64-byte callback_data) deliver as undefined. The registry now returns the clicked element value from dispatch, and create-bot falls back to it when the event has none. Slack (value-in-payload) is unchanged.
Bring the Slack adapter up to the current native streaming API surface
(chat.startStream/appendStream/stopStream, GA Oct 2025) and remove the
type-erasure workarounds.
- Remove `as unknown as Parameters<...>` casts in favor of the SDK's typed
args (ChatStartStream/AppendStream/StopStream/PostMessage/UpdateArguments).
- Stream a whole turn into ONE message: drop the per-message continuation
splitting (no documented cumulative cap; matches vercel/chat), keeping the
12k per-append chunking.
- Surface tool progress as native in-message `task_update` chunks
(task_display_mode "timeline"), degrading to `🔧` rows where
structured chunks are unavailable.
- Add opt-in AI feedback buttons via `slack({ feedback })` — a typed
context_actions/feedback_buttons row attached at stopStream, with clicks
routed adapter-locally (bypassing the engine's interaction dispatch).
- Scope recipient_user_id/recipient_team_id to channel targets only.
- Lower the native flush floor to ~600ms (appendStream Tier-4), legacy stays
800ms.
Engine: add an optional, backward-compatible RunRenderer.finish() hook called
after runAgentLoop so a turn-scoped renderer can finalize its stream.
Update package READMEs + ARCHITECTURE for the assistant pane, native streaming,
and the new onThreadStarted / setSuggestedPrompts / setTitle surface. Reverse
the slack.mdx callout that told users to delete the assistant scopes (now
required), and enable the pane in the examples/slack manifest (assistant_view +
assistant:write + assistant_thread_* events) with a dev-ex onThreadStarted
greeting and the assistant config.
Activate Slack's assistant pane and native streaming as the default experience,
with zero config and safe degradation. The portable surface lands in
@copilotkit/bot (onThreadStarted lifecycle, capability-gated
thread.setSuggestedPrompts/setTitle, two SurfaceCapabilities flags + optional
adapter methods); @copilotkit/bot-slack adds the Bolt Assistant middleware
(assistant.ts), the chat.startStream transport with automatic legacy fallback
(native-stream.ts), a per-thread no-double-delivery listener guard, and
renderer pane-status mode. Everything degrades, never throws.
createBot with handler registration (onMention/onMessage/onInterrupt/
onCommand), the agent run/tool/interrupt loop, content-stable JSX
action binding with cold-path rehydration from a pluggable ActionStore,
the PlatformAdapter boundary, capability-gated thread methods, one
shared BotToolContext, defineBotTool / defineBotCommand, and typed
interaction/interrupt handlers. Includes fake-adapter/fake-agent
testing utilities.