github slackapi/bolt-js @slack/bolt@4.5.0

10 hours ago

AI-Enabled Features: Loading States, Text Streaming, and Feedback Buttons

🍿 Preview

2025-10-06-loading-state-text-streaming-feedback.mov

⚡ Getting Started

Try the AI Agent Sample app to explore the AI-enabled features and existing Assistant helper:

# Create a new AI Agent app
$ slack create slack-ai-agent-app --template slack-samples/bolt-js-assistant-template
$ cd slack-ai-agent-app/

# Add your OPENAI_API_KEY
$ export OPENAI_API_KEY=sk-proj-ahM...

# Run the local dev server
$ slack run

⌛ Loading States

Loading states allows you to not only set the status (e.g. "My app is typing...") but also sprinkle some personality by cycling through a collection of loading messages:

Web Client SDK:

app.event('message', async ({ client, context, event, logger }) => {
    // ...
    await client.assistant.threads.setStatus({
        channel_id: channelId,
        thread_ts: threadTs,
        status: 'thinking...',
        loading_messages: [
            'Teaching the hamsters to type faster…',
            'Untangling the internet cables…',
            'Consulting the office goldfish…',
            'Polishing up the response just for you…',
            'Convincing the AI to stop overthinking…',
        ],
    });

    // Start a new message stream
});

Assistant Class:

const assistant = new Assistant({
    threadStarted: assistantThreadStarted,
    threadContextChanged: assistantThreadContextChanged,
    userMessage: async ({ client, context, logger, message, getThreadContext, say, setTitle, setStatus }) => {
        await setStatus({
            status: 'thinking...',
            loading_messages: [
                'Teaching the hamsters to type faster…',
                'Untangling the internet cables…',
                'Consulting the office goldfish…',
                'Polishing up the response just for you…',
                'Convincing the AI to stop overthinking…',
            ],
        });
    },
});

🔮 Text Streaming Helper

The client.chatStream() helper utility can be used to streamline calling the 3 text streaming methods:

app.event('message', async ({ client, context, event, logger }) => {
    // ...

    // Start a new message stream
    const streamer = client.chatStream({
        channel: channelId,
        recipient_team_id: teamId,
        recipient_user_id: userId,
        thread_ts: threadTs,
    });

    // Loop over OpenAI response stream
    // https://platform.openai.com/docs/api-reference/responses/create
    for await (const chunk of llmResponse) {
        if (chunk.type === 'response.output_text.delta') {
            await streamer.append({
                markdown_text: chunk.delta,
            });
        }
    }

    // Stop the stream and attach feedback buttons
    await streamer.stop({ blocks: [feedbackBlock] });
});

🔠 Text Streaming Methods

Alternative to the Text Streaming Helper is to call the individual methods.

1) client.chat.startStream

First, start a chat text stream to stream a response to any message:

app.event('message', async ({ client, context, event, logger }) => {
    // ...
    const streamResponse = await client.chat.startStream({
        channel: channelId,
        recipient_team_id: teamId,
        recipient_user_id: userId,
        thread_ts: threadTs,
    });

    const streamTs = streamResponse.ts

2) client.chat.appendStream

After starting a chat text stream, you can then append text to it in chunks (often from your favourite LLM SDK) to convey a streaming effect:

for await (const chunk of llmResponse) {
    if (chunk.type === 'response.output_text.delta') {
        await client.chat.appendSteam({
            channel: channelId,
            markdown_text: chunk.delta,
            ts: streamTs,
        });
    }
}

3) client.chat.stopStream

Lastly, you can stop the chat text stream to finalize your message:

await client.chat.stopStream({
    blocks: [feedbackBlock],
    channel: channelId,
    ts: streamTs,
});

👍🏻 Feedback Buttons

Add feedback buttons to the bottom of a message, after stopping a text stream, to gather user feedback:

const feedbackBlock = {
    type: 'context_actions',
    elements: [{
        type: 'feedback_buttons',
        action_id: 'feedback',
        positive_button: {
            text: { type: 'plain_text', text: 'Good Response' },
            accessibility_label: 'Submit positive feedback on this response',
            value: 'good-feedback',
        },
        negative_button: {
            text: { type: 'plain_text', text: 'Bad Response' },
            accessibility_label: 'Submit negative feedback on this response',
            value: 'bad-feedback',
        },
    }],
};

// Using the Text Streaming Helper
await streamer.stop({ blocks: [feedbackBlock] });
// Or, using the Text Streaming Method
await client.chat.stopStream({
    blocks: [feedbackBlock],
    channel: channelId,
    ts: streamTs,
});

What's Changed

👾 Enhancements

  • feat: add ai-enabled features text streaming methods, feedback blocks, and loading state in #2674 - Thanks @zimeg!

🐛 Bug fixes

  • Fix: allows Assistant say function to properly pass metadata in #2569 - Thanks @jamessimone!
  • fix: better ES module support for App class in #2632 - Thanks @malewis5!
  • fix(typescript): accept empty list of suggested prompts for the assistant class in #2650 - Thanks @zimeg!

📚 Documentation

  • docs(fix): redirect links for project package migration guides to the tools site in #2539 - Thanks @zimeg!
  • Docs: moved over custom steps dynamic options page. in #2552 - Thanks @technically-tracy!
  • docs: updated nav in #2564 - Thanks @technically-tracy!
  • docs: rotate tokens on a separate schedule with the oauth package in #2558 - Thanks @zimeg!
  • docs: guide quick start app creation using the slack cli in #2535 - Thanks @zimeg!
  • Update event-listening.md in #2584 - Thanks @jfbn!
  • docs: Update language around AI apps in #2606 - Thanks @haleychaas!
  • docs: fix listener middleware function and use global middleware examples in #2610 - Thanks @zimeg!
  • docs: update ai hugging face tutorial examples in #2613 - Thanks @zimeg!
  • docs: guide building an app tutorial using the slack cli in #2597 - Thanks @zimeg!
  • docs: use the app logger in examples in #2651 - Thanks @zimeg!
  • docs: updates for combined quickstart in #2661 - Thanks @haleychaas!

🤖 Dependencies

  • Update axios dependency to version ^1.12.0 in #2657 - Thanks @malewis5!
  • chore(deps): update @slack/web-api requirement from ^7.9.1 to ^7.9.2 in #2541 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/deploy-heroku in #2542 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/getting-started-typescript in #2543 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/oauth in #2544 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/oauth-express-receiver in #2545 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/socket-mode-oauth in #2546 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/deploy-aws-lambda in #2547 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/socket-mode in #2548 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/bolt requirement from ^4.3.0 to ^4.4.0 in /examples/message-metadata in #2549 - Thanks @dependabot[bot]!
  • chore(deps): bump the docusaurus group in /docs with 5 updates in #2553 - Thanks @dependabot[bot]!
  • chore(deps): bump brace-expansion from 1.1.11 to 1.1.12 in /docs in #2574 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/web-api requirement from ^7.9.2 to ^7.9.3 in #2578 - Thanks @dependabot[bot]!
  • chore(deps): bump dotenv from 16.6.1 to 17.0.0 in /examples/getting-started-typescript in #2589 - Thanks @dependabot[bot]!
  • chore(deps): bump dotenv from 16.6.1 to 17.0.0 in /examples/custom-receiver in #2590 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/types requirement from ^2.14.0 to ^2.15.0 in #2600 - Thanks @dependabot[bot]!
  • chore(deps): bump on-headers and compression in /docs in #2607 - Thanks @dependabot[bot]!
  • chore(deps): bump slackapi/slack-github-action from 2.1.0 to 2.1.1 in #2615 - Thanks @dependabot[bot]!
  • chore(deps): bump @koa/router from 13.1.1 to 14.0.0 in /examples/custom-receiver in #2622 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/types requirement from ^2.15.0 to ^2.16.0 in #2633 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/web-api requirement from ^7.9.3 to ^7.10.0 in #2634 - Thanks @dependabot[bot]!
  • chore(deps): bump codecov/codecov-action from 5.4.3 to 5.5.0 in #2643 - Thanks @dependabot[bot]!
  • chore(deps): bump actions/checkout from 4.2.2 to 5.0.0 in #2642 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/socket-mode requirement from ^2.0.4 to ^2.0.5 in #2646 - Thanks @dependabot[bot]!
  • chore(deps): update @slack/oauth requirement from ^3.0.3 to ^3.0.4 in #2647 - Thanks @dependabot[bot]!
  • chore(deps): bump codecov/codecov-action from 5.5.0 to 5.5.1 in #2662 - Thanks @dependabot[bot]!
  • chore(deps): bump actions/stale from 9.1.0 to 10.0.0 in #2663 - Thanks @dependabot[bot]!
  • chore(deps): bump actions/setup-node from 4.4.0 to 5.0.0 in #2664 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 22.15.19 to 22.15.21 in #2540 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.14.3 to ^4.14.4 in /examples/deploy-aws-lambda in #2554 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump stylelint from 16.19.1 to 16.20.0 in /docs in #2561 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.14.4 to ^4.15.1 in /examples/deploy-aws-lambda in #2562 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 22.15.21 to 22.15.29 in #2563 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 22.15.29 to 22.15.30 in #2566 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.15.1 to ^4.17.0 in /examples/deploy-aws-lambda in #2567 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 22.15.30 to 24.0.3 in #2570 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 22.15.32 to 24.0.3 in /examples/getting-started-typescript in #2571 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 22.15.32 to 24.0.3 in /examples/custom-receiver in #2572 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.17.0 to ^4.17.1 in /examples/deploy-aws-lambda in #2577 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.0.3 to 24.0.7 in #2591 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump stylelint from 16.20.0 to 16.21.0 in /docs in #2593 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.0.7 to 24.0.10 in #2594 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump stylelint from 16.21.0 to 16.21.1 in /docs in #2596 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.0.10 to 24.0.14 in #2604 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.0.14 to 24.0.15 in #2609 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump stylelint-config-standard from 38.0.0 to 39.0.0 in /docs in #2617 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.0.15 to 24.2.0 in #2619 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.17.1 to ^4.17.2 in /examples/deploy-aws-lambda in #2621 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump typescript from 5.8.3 to 5.9.2 in /examples/custom-receiver in #2620 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump typescript from 5.8.3 to 5.9.2 in /examples/getting-started-typescript in #2623 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.17.2 to ^4.18.0 in /examples/deploy-aws-lambda in #2624 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.2.0 to 24.2.1 in #2626 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.2.1 to 24.3.0 in #2635 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.18.0 to ^4.18.1 in /examples/deploy-aws-lambda in #2636 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.3.0 to 24.3.1 in #2648 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.3.1 to 24.4.0 in #2655 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.4.0 to 24.5.2 in #2658 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.18.1 to ^4.19.1 in /examples/deploy-aws-lambda in #2659 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump @types/node from 24.5.2 to 24.7.0 in #2669 - Thanks @dependabot[bot]!
  • chore(deps-dev): update serverless requirement from ^4.19.1 to ^4.20.2 in /examples/deploy-aws-lambda in #2670 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump typescript from 5.9.2 to 5.9.3 in /examples/custom-receiver in #2671 - Thanks @dependabot[bot]!
  • chore(deps-dev): bump typescript from 5.9.2 to 5.9.3 in /examples/getting-started-typescript in #2672 - Thanks @dependabot[bot]!

🧰 Maintaince

  • ci: pin actions workflow step hashes and use minimum permissions in #2537 - Thanks @zimeg!
  • chore: update dependabot to open one PR for all @slack dependencies in /examples in #2550 - Thanks @WilliamBergamin!
  • Revert "chore: update dependabot to open one PR for all @slack depend… in #2551 - Thanks @WilliamBergamin!
  • build: clone repository "docs" and configuration when syncing project docs + removing separate docusaurus build in #2586 - Thanks @lukegalbraithrussell!
  • Revert "build: clone repository "docs" and configuration when syncing project docs + removing separate docusaurus build" in #2595 - Thanks @lukegalbraithrussell!
  • refactor: check payload type before extracting assistant thread info in #2603 - Thanks @zimeg!
  • ci: send a notification of scheduled failing tests in #2601 - Thanks @zimeg!
  • ci: output verbose details when installing packages for tests in #2602 - Thanks @zimeg!
  • Build: remove docusaurus configuration files in #2614 - Thanks @lukegalbraithrussell!
  • test: swap rewiremock with proxyquire in #2629 - Thanks @WilliamBergamin!
  • ci: post regression notifications if scheduled tests do not succeed in #2667 - Thanks @zimeg!
  • chore(release): version @slack/bolt@4.5.0 in #2675 - Thanks @zimeg!

New Contributors 🎉

Milestone: https://github.com/slackapi/bolt-js/milestone/59?closed=1
Full Changelog: https://github.com/slackapi/bolt-js/compare/@slack/bolt@4.4.0...@slack/bolt@4.5.0

Don't miss a new bolt-js release

NewReleases is sending notifications on new releases.