Skip to content

msteams: add typingIndicator config and prevent duplicate DM typing indicator#60771

Merged
BradGroux merged 5 commits intoopenclaw:mainfrom
BradGroux:bradgroux/msteams-typing-config-60746-56380
Apr 4, 2026
Merged

msteams: add typingIndicator config and prevent duplicate DM typing indicator#60771
BradGroux merged 5 commits intoopenclaw:mainfrom
BradGroux:bradgroux/msteams-typing-config-60746-56380

Conversation

@BradGroux
Copy link
Copy Markdown
Contributor

@BradGroux BradGroux commented Apr 4, 2026

Summary

  • add channels.msteams.typingIndicator config flag (default true)
  • gate native typing activity on typingIndicator and active stream state
  • avoid duplicate typing UX in DMs by skipping native typing when stream status is active
  • add focused tests for channel default behavior and opt-out behavior
  • fix Zod schema placement so typingIndicator validates under MSTeamsConfigSchema

Why

Testing

  • pnpm config:docs:gen
  • pnpm vitest run extensions/msteams/src/reply-dispatcher.test.ts --config vitest.extensions.config.ts

Fixes #56380
Fixes #60746

Copilot AI review requested due to automatic review settings April 4, 2026 08:48
@openclaw-barnacle openclaw-barnacle bot added channel: msteams Channel integration: msteams size: XS maintainer Maintainer-authored PR labels Apr 4, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7c36c483d5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
textChunkLimit: z.number().int().positive().optional(),
chunkMode: z.enum(["length", "newline"]).optional(),
typingIndicator: z.boolean().optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move typingIndicator field to MSTeams config schema

The added typingIndicator property is currently wired into DiscordAccountSchema, but the new behavior reads channels.msteams.typingIndicator and MSTeamsConfigSchema is strict. That means users who set channels.msteams.typingIndicator will get a validation error for an unknown key, so the new Teams config knob cannot actually be used, while an unrelated Discord account key is now accepted but ignored.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 4, 2026

Greptile Summary

This PR adds a typingIndicator config knob to MS Teams and fixes a duplicate typing indicator issue in DMs by skipping the native typing activity when a stream is already active. The implementation in reply-dispatcher.ts and the TypeScript type in types.msteams.ts are correct, and the new tests provide good coverage.

However, there is a critical bug in the Zod schema placement:

  • Wrong schema: typingIndicator: z.boolean().optional() was added to DiscordAccountSchema (~line 498 of zod-schema.providers-core.ts) instead of MSTeamsConfigSchema (~line 1536).
  • Impact: MSTeamsConfigSchema uses .strict(), so any user who configures channels.msteams.typingIndicator: false will receive a Zod "Unrecognized key" validation error, making the config knob completely non-functional in practice.
  • Fix needed: Move the field from DiscordAccountSchema to MSTeamsConfigSchema, adjacent to the existing blockStreaming field, and re-run pnpm config:docs:gen to update the baseline artifact per the repo's drift-check flow.

Confidence Score: 1/5

Not safe to merge — the Zod validation schema was added to the wrong channel schema, breaking the config knob entirely for MS Teams users.

The core logic change and TypeScript type are correct, but the Zod schema for typingIndicator was mistakenly placed in DiscordAccountSchema instead of MSTeamsConfigSchema. Since MSTeamsConfigSchema uses .strict(), any user trying to opt out via channels.msteams.typingIndicator: false will get a config validation error. The feature cannot be used as documented until this is corrected.

src/config/zod-schema.providers-core.tstypingIndicator must be moved from DiscordAccountSchema to MSTeamsConfigSchema.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/config/zod-schema.providers-core.ts
Line: 498

Comment:
**`typingIndicator` added to the wrong Zod schema**

This property was added to `DiscordAccountSchema` (line 479), but the feature is implemented in the MS Teams extension and should be in `MSTeamsConfigSchema` (around line 1536).

Because `MSTeamsConfigSchema` uses `.strict()` (line 1560), any user who sets `channels.msteams.typingIndicator: false` in their config will receive a Zod validation error like `"Unrecognized key(s) in object: 'typingIndicator'"` — completely preventing the config knob from working.

The fix is to remove the field from `DiscordAccountSchema` and add it to `MSTeamsConfigSchema` instead, adjacent to the existing `blockStreaming` field:

```
// In MSTeamsConfigSchema (~line 1536):
    chunkMode: z.enum(["length", "newline"]).optional(),
    typingIndicator: z.boolean().optional(),
    blockStreaming: z.boolean().optional(),
```

And remove it from `DiscordAccountSchema`:
```
// In DiscordAccountSchema (~line 497):
    chunkMode: z.enum(["length", "newline"]).optional(),
    blockStreaming: z.boolean().optional(),
```

Note: `pnpm config:docs:gen` should also be re-run to keep the baseline artifact in sync (per repo CLAUDE.md guidelines).

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "msteams: add typingIndicator config and ..." | Re-trigger Greptile

dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
textChunkLimit: z.number().int().positive().optional(),
chunkMode: z.enum(["length", "newline"]).optional(),
typingIndicator: z.boolean().optional(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 typingIndicator added to the wrong Zod schema

This property was added to DiscordAccountSchema (line 479), but the feature is implemented in the MS Teams extension and should be in MSTeamsConfigSchema (around line 1536).

Because MSTeamsConfigSchema uses .strict() (line 1560), any user who sets channels.msteams.typingIndicator: false in their config will receive a Zod validation error like "Unrecognized key(s) in object: 'typingIndicator'" — completely preventing the config knob from working.

The fix is to remove the field from DiscordAccountSchema and add it to MSTeamsConfigSchema instead, adjacent to the existing blockStreaming field:

// In MSTeamsConfigSchema (~line 1536):
    chunkMode: z.enum(["length", "newline"]).optional(),
    typingIndicator: z.boolean().optional(),
    blockStreaming: z.boolean().optional(),

And remove it from DiscordAccountSchema:

// In DiscordAccountSchema (~line 497):
    chunkMode: z.enum(["length", "newline"]).optional(),
    blockStreaming: z.boolean().optional(),

Note: pnpm config:docs:gen should also be re-run to keep the baseline artifact in sync (per repo CLAUDE.md guidelines).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config/zod-schema.providers-core.ts
Line: 498

Comment:
**`typingIndicator` added to the wrong Zod schema**

This property was added to `DiscordAccountSchema` (line 479), but the feature is implemented in the MS Teams extension and should be in `MSTeamsConfigSchema` (around line 1536).

Because `MSTeamsConfigSchema` uses `.strict()` (line 1560), any user who sets `channels.msteams.typingIndicator: false` in their config will receive a Zod validation error like `"Unrecognized key(s) in object: 'typingIndicator'"` — completely preventing the config knob from working.

The fix is to remove the field from `DiscordAccountSchema` and add it to `MSTeamsConfigSchema` instead, adjacent to the existing `blockStreaming` field:

```
// In MSTeamsConfigSchema (~line 1536):
    chunkMode: z.enum(["length", "newline"]).optional(),
    typingIndicator: z.boolean().optional(),
    blockStreaming: z.boolean().optional(),
```

And remove it from `DiscordAccountSchema`:
```
// In DiscordAccountSchema (~line 497):
    chunkMode: z.enum(["length", "newline"]).optional(),
    blockStreaming: z.boolean().optional(),
```

Note: `pnpm config:docs:gen` should also be re-run to keep the baseline artifact in sync (per repo CLAUDE.md guidelines).

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an MS Teams configuration switch for native typing activities and updates the Teams reply dispatcher to avoid showing duplicate typing UX in DMs (stream status + native typing).

Changes:

  • Introduce channels.msteams.typingIndicator config flag (intended default: enabled).
  • Skip native typing callback in DMs when the Teams reply stream is active to prevent double indicators.
  • Add/adjust unit tests covering default DM behavior and opt-out behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/config/zod-schema.providers-core.ts Adds a typingIndicator field, but currently in the wrong schema block (needs to be in MSTeamsConfigSchema, not Discord).
src/config/types.msteams.ts Adds typingIndicator?: boolean to the MS Teams config type (docstring needs a small accuracy tweak).
extensions/msteams/src/reply-dispatcher.ts Gates typingCallbacks.onReplyStart based on typingIndicator and stream presence to prevent duplicate DM typing indicators.
extensions/msteams/src/reply-dispatcher.test.ts Adds tests asserting DM suppresses native typing, and that typing can be disabled via config (test scenario could better reflect typing-supported conversation types).

dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
textChunkLimit: z.number().int().positive().optional(),
chunkMode: z.enum(["length", "newline"]).optional(),
typingIndicator: z.boolean().optional(),
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typingIndicator appears to be an MS Teams config flag, but it’s being added to DiscordAccountSchema here. Since MSTeamsConfigSchema is .strict() and currently does not include typingIndicator, setting channels.msteams.typingIndicator would still fail config validation (and Discord would gain an unused field). Move this field into MSTeamsConfigSchema (and remove it from Discord), then regenerate any derived config metadata/schema artifacts as needed.

Suggested change
typingIndicator: z.boolean().optional(),

Copilot uses AI. Check for mistakes.
textChunkLimit?: number;
/** Chunking mode: "length" (default) splits by size; "newline" splits on every newline. */
chunkMode?: "length" | "newline";
/** Send native Teams typing indicator before replies. Default: true for groups/channels; DMs use informative stream status. */
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says “Default: true for groups/channels; DMs use informative stream status”, but in the runtime implementation native typing is only supported for personal and groupchat conversation types (not channel). Consider rewording to “group chats” (and clarify that 1:1 chats suppress native typing when the informative status stream is used).

Suggested change
/** Send native Teams typing indicator before replies. Default: true for groups/channels; DMs use informative stream status. */
/** Send native Teams typing indicator before replies. Default: true for group chats; 1:1 chats use informative stream status instead of native typing. */

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +161
it("sends native typing indicator for channel conversations by default", async () => {
createDispatcher("channel");
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];

await options.onReplyStart?.();

expect(streamInstances).toHaveLength(0);
expect(typingCallbacks.onReplyStart).toHaveBeenCalledTimes(1);
});

it("skips native typing indicator when typingIndicator=false", async () => {
createDispatcher("channel", { typingIndicator: false });
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];

await options.onReplyStart?.();

expect(typingCallbacks.onReplyStart).not.toHaveBeenCalled();
});
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test asserts native typing behavior for conversationType="channel", but the dispatcher only supports typing activities for personal and groupchat conversations (channel uses a no-op typing sender). Using groupchat here (and in the opt-out test) would better match real typing-supported scenarios and reduce the risk of the test passing even if typing is never actually emitted for supported conversation types.

Copilot uses AI. Check for mistakes.
@BradGroux BradGroux self-assigned this Apr 4, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b90bcb5980

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +182 to +183
if (typingIndicatorEnabled && !streamController.hasStream()) {
await typingCallbacks?.onReplyStart?.();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fallback to native typing when informative update fails

onReplyStart now suppresses typingCallbacks.onReplyStart() for every personal chat based only on hasStream(), but sendInformativeUpdate() swallows send failures internally (onError + continue). In DMs where the streaminfo typing activity is rejected or fails transiently, this branch skips both informative status and native typing, which is a regression from previous behavior where native typing still ran. Please gate the skip on a successful informative send (or otherwise keep native typing as a fallback) so DM users still get typing feedback when stream initialization fails.

Useful? React with 👍 / 👎.

@BradGroux BradGroux merged commit fce81fc into openclaw:main Apr 4, 2026
9 checks passed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c32216eb8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +44 to +45
if (attachment.contentType !== "text/html") {
continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match HTML attachments by prefix instead of exact MIME

extractTextFromHtmlAttachments only accepts attachment.contentType === "text/html", so attachments reported as text/html; charset=utf-8 are skipped and their message text is never extracted. In those cases rawText often stays empty and the handler falls back to attachment placeholders, which can make normal text messages look like file-only messages and bypass expected text processing.

Useful? React with 👍 / 👎.

Comment on lines +50 to +53
: typeof attachment.content?.text === "string"
? attachment.content.text
: typeof attachment.content?.body === "string"
? attachment.content.body
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse the content.content HTML payload variant

The new fallback extractor only checks attachment.content.text and attachment.content.body, but Teams attachments in this codebase also support attachment.content.content (see the shared HTML extraction utility). When that variant arrives, this branch returns an empty string and the inbound message text is lost even though valid HTML content is present.

Useful? React with 👍 / 👎.

KimGLee pushed a commit to KimGLee/openclaw that referenced this pull request Apr 4, 2026
…ndicator (openclaw#60771)

* msteams: add typingIndicator config and avoid duplicate DM typing

* fix(msteams): validate typingIndicator config

* fix(msteams): stop streaming before Teams timeout

* fix(msteams): classify expired streams correctly

* fix(msteams): handle link text from html attachments

---------

Co-authored-by: Brad Groux <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Microsoft Teams: add config option to disable typing indicator before replies msteams: Double typing indicator in Teams DMs

2 participants