* Fix JSX issues
* fix: add null guards for event.thread in onAction handlers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(slack): support streaming task_update, plan_update, markdown_text chunks
- Add StreamChunk type for structured streaming content
- Extend stream() to accept AsyncIterable<string | StreamChunk>
- Add taskDisplayMode option to StreamOptions
- Slack adapter passes structured chunks directly to Slack's streaming API
- Handle url_verification before signature check to prevent Slack from
silently disabling event delivery when signing secret is misconfigured
- Add tests for StreamChunk handling in thread.post()
* review fixes: type safety, security, dedup
- Replace loose StreamChunk interface (index signature) with
discriminated union (MarkdownTextChunk | TaskUpdateChunk | PlanUpdateChunk)
- Move url_verification back after signature check to prevent
unauthenticated challenge responses
- Remove redundant AsyncIterable<string> from PostableMessage union
- Extract pushTextAndFlush helper to deduplicate markdown flush logic
- Remove unnecessary `as string` casts after type guards
- Export individual chunk types from package index
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* add changeset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix openModal crash when action has no thread context
Home tab actions (e.g. Change Team) have no thread → openModal closure
crashed accessing thread.channel on null. Guard thread access in the
openModal closure and pass undefined to storeModalContext.
Add test for empty threadId action → openModal works with null thread.
* add changeset
* make ActionEvent.thread nullable, remove unsafe cast
- Change `ActionEvent.thread` type from `Thread` to `Thread | null`
- Remove `null as unknown as Thread` unsafe cast in handleActionEvent
- Update test files to use null guards and optional chaining
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: preserve nesting depth when rendering lists in all chat adapters
All format converters (Slack, Discord, Teams, Google Chat) were flattening
nested lists during Markdown→platform conversion. When a listItem had a
paragraph followed by a child list, the child list output was concatenated
directly onto the parent item's text with no indentation or separating
newline, producing garbled output like "• parent• child 1\n• child 2".
Add an optional `depth` parameter to each adapter's private `nodeToX()`
method. The `isListNode` handler now builds lines explicitly: paragraph
content is prefixed with the bullet/number at the correct indent level
(`" ".repeat(depth)`), and nested list nodes are rendered recursively
at `depth + 1`. Sibling items at the same depth are unaffected.
Add regression tests covering:
- nested unordered lists (2 levels)
- nested ordered lists (2 levels)
- deeply nested lists (3 levels)
- flat sibling items (no regressions)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: extract renderList() helper to eliminate dead depth parameter
The previous fix added `depth = 0` to each adapter's general nodeToX()
method, but only the isListNode branch ever used it — a dead parameter
across ~10 other branches that made the signature misleading.
Extract a private renderList(node: List, depth: number) method in each
adapter. nodeToX() stays as a clean single-argument node converter;
all depth-aware list logic lives exclusively inside renderList(), which
recurses into itself for nested lists and calls nodeToX() for leaf nodes.
Import the List type from the chat package to give renderList() an
explicit, narrowed parameter type.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Deduplicate renderList into BaseFormatConverter, fix start offset, add mixed list tests
- Extract shared renderList() into BaseFormatConverter with configurable bullet char
- Respect List.start property for ordered lists starting at non-1 values
- Remove dead isListItemNode branches from all adapters
- Add mixed ordered/unordered nesting test to all adapters
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
* Add Telegram auto mode polling and init username fallback
* Expose Telegram resetWebhook API
* Refine Telegram API to longPolling and safer auto mode
* Fix: Environment variable cleanup sets `process.env.VERCEL` to the string `"undefined"` (truthy) instead of removing it, leaking a truthy VERCEL env var into subsequent tests.
This commit fixes the issue reported at packages/adapter-telegram/src/index.test.ts:661
**Bug explanation:**
In the test "auto mode stays in webhook mode on serverless runtime" (line 622), the cleanup code in the `finally` block at line 662 uses `process.env.VERCEL = undefined` when the env var wasn't previously set. In Node.js, `process.env` coerces all values to strings, so `process.env.VERCEL = undefined` actually sets `process.env.VERCEL` to the string `"undefined"`. This is truthy (`Boolean("undefined")` === `true`).
This was verified empirically:
```
process.env.TEST_VAR = undefined;
typeof process.env.TEST_VAR // "string"
process.env.TEST_VAR // "undefined"
Boolean(process.env.TEST_VAR) // true
```
The consequence is that after this test runs, `process.env.VERCEL` remains set to the truthy string `"undefined"`, causing `isLikelyServerlessRuntime()` to return `true` for all subsequent tests that run in the same process. This could cause any auto-mode tests that follow to incorrectly detect a serverless runtime and behave differently than expected.
**Fix explanation:**
Changed `process.env.VERCEL = undefined` to `delete process.env.VERCEL`, which properly removes the environment variable from `process.env`. After `delete`, `process.env.VERCEL` is `undefined` (the actual undefined value, not the string), and `Boolean(process.env.VERCEL)` correctly returns `false`. This matches the standard pattern for cleaning up environment variables in Node.js tests.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: timolins <me@timo.sh>
* Fix lint: replace delete env cleanup with Reflect.deleteProperty
* Harden Telegram polling tests and polish polling docs
* Improve Telegram polling docs with clearer motivation and examples
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(chat, adapter-gchat): add disabled prop to Button
Google Chat Cards v2 natively supports `disabled` on buttons, but
`ButtonOptions` / `ButtonElement` didn't expose it. Add the prop to
the core type and thread it through the gchat adapter serializer.
Closes#149
Made-with: Cursor
* feat(adapter-discord): thread disabled prop through Discord button serializer
DiscordButton already had `disabled` in its type but convertButtonElement
never set it from ButtonElement. Wire it through so disabled buttons
render correctly on Discord too.
Made-with: Cursor
* feat(chat): allow disabling fallback streaming placeholder
Add ChatConfig options to delay initial post+edit placeholder and wait for real text before creating the streamed message. Improves ordering for Telegram and other fallback-streaming adapters.
Made-with: Cursor
* fix(chat): simplify fallback streaming placeholder
* Create fallback-streaming-placeholder.md
* Improve test
* Add new test
* Run fix
* Update streaming.mdx
---------
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
* fix(discord): add Partials.Channel to gateway client for DM support
The gateway client requests DirectMessages intent but doesn't include `Partials.Channel`, which discord.js requires to emit DM events. Without it, all DM `MessageCreate` events are silently dropped.
Signed-off-by: TRACTION <19631364+iamtraction@users.noreply.github.com>
* Create fix-discord-dm-partials.md
* Create gateway.test.ts
* Run fix
---------
Signed-off-by: TRACTION <19631364+iamtraction@users.noreply.github.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
* refactor: replace broad googleapis dependency with specific packages
* fix: reorder imports in gchat adapter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Create tidy-groups-marry.md
---------
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add support for telegram bot
* Add support for action buttons in telegram
* Use markdown parse mode
* Add more tests
* Fix linting issues
* Fixes
* Fixes
* Don't reverse order messages for AI and always answer for telegram
* Add telegram integration test
* Update telegram.json
* feat(chat): make message dedup TTL configurable to prevent Slack retry duplicates
* changeset
* fix
* bump default to 5 min
* fix
* Fix: Test, JSDoc, and README all reference the old default dedupe TTL of 60,000ms after the constant was changed to 300,000ms (5 minutes), causing test failure.
This commit fixes the issue reported at packages/chat/src/chat.test.ts:140
**Bug:**
The `DEDUPE_TTL_MS` constant in `packages/chat/src/chat.ts` line 51 was changed from `60_000` (60 seconds) to `5 * 60 * 1000` (300,000 ms = 5 minutes). However, three other locations were not updated to match:
1. **Test (`chat.test.ts` line 140):** The test "should use default dedupe TTL of 60 seconds" asserts that `mockState.set` is called with `60_000` as the TTL. Since the actual code now uses `300_000`, the test fails — confirmed by running the test suite which shows 1 failed test: expected `60000` but received `300000`.
2. **JSDoc (`types.ts` line 40):** The documentation comment says "Defaults to 60000 (60 seconds)" which is incorrect — it's now 300000 (5 minutes).
3. **README.md (line 52):** The Configuration table lists the default as `60000` which is now wrong.
**Evidence:** Running `npx vitest run` shows 1 test failure out of 443 tests. After applying the fix, all 443 tests pass.
**Fix:**
1. Updated the test in `chat.test.ts`: Changed the test description from "60 seconds" to "5 minutes" and the expected value from `60_000` to `300_000`.
2. Updated the JSDoc in `types.ts`: Changed "Defaults to 60000 (60 seconds)" to "Defaults to 300000 (5 minutes)".
3. Updated the README.md: Changed the default value in the Configuration table from `60000` to `300000`.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: NicolasMontone <montonenicolas01@gmail.com>
* fix
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>