* fix(msteams): preserve replies across ingress replay
Recovered Teams channel and group-chat responses now preserve reply and quote context across durable ingress replay. Discovery metadata also advertises the existing group and reaction capabilities.
* chore(msteams): document replay assertion safety
* test(msteams): normalize replay delivery errors
* fix(msteams): dedupe poll vote selections before maxSelections truncation
normalizeMSTeamsPollSelections truncated the raw selections to
maxSelections before deduplicating, so a duplicated choice (e.g. an
Adaptive Card posting "0,0,1") consumed multiple slots and starved
later distinct choices: with maxSelections=2 the stored vote became
["0"] and choice "1" was silently dropped. Deduplicate first, then
truncate.
* fix(msteams): simplify poll selection normalization
---------
Co-authored-by: ClawSweeper <steipete+clawsweeper@gmail.com>
* refactor: consolidate coercion ownership
Centralize four canonical coercion helpers, migrate exact core and plugin duplicates through narrow Plugin SDK facades, and enforce declaration and plugin-normalization ownership boundaries.
The sweep adds eight focused SDK exports while deleting more production and tooling code than it adds. User-visible behavior is unchanged except for safer equivalent object and UI parsing at existing boundaries.
* fix: guard integer option ownership
Register resolveIntegerOption with the canonical function owner and extend the declaration-guard fixture so future local duplicates fail validation.
* fix: keep integer helpers on numeric facade
Remove the unshipped duplicate string-coerce exports and route every affected plugin consumer through the existing number-runtime contract.
* fix: point numeric coercion to number runtime
Make boundary and declaration diagnostics recommend the canonical numeric facade, with failing-before coverage for both guidance paths.
Unset heartbeat.target now resolves "owner": elected heartbeat notifications deliver to the operator's DM resolved from commands.ownerAllowFrom or the channel allowFrom (first concrete entry; wildcards and channel-scoped wildcards excluded; configured owners exhausted across channels before any channel-local fallback). Delivery requires the channel's own classifier to positively prove a direct destination — every bundled messaging plugin now ships an inferTargetChatType contract — and unproven or group-shaped destinations fail closed to the visible no-route state. The first implicitly-routed delivery carries a one-line self-explanation naming the target: "none" opt-out. Explicit target "last" remains as the follow-the-conversation opt-in. Refines the unreleased #121892 default before it ships; refs #121880.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Keep raw commands, paths, and provider errors out of ordinary chat while preserving explicit raw diagnostics and structured admin history.
Default command progress is status-only; `/verbose full` and `commandText: "raw"` retain diagnostic detail.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* refactor(test): exercise real Teams monitor lifecycle
* refactor(test): use real gateway lifecycle state
* refactor(test): use real restart delivery queue
* test: type Teams SDK boundary fixture
* test(msteams): serve JWKS from in-process server in auth coverage
The Entra/service-token validator tests spied JwksClient.prototype on the
jwks-rsa copy the test file imports, but the SDK's internal
require("jwks-rsa") resolves a different physical copy and vitest cannot
intercept a CommonJS require inside node_modules — so the spy missed and
the tests hit the real login.botframework.com / login.microsoftonline.com
JWKS endpoints (JwksError: Bad Request, SigningKeyNotFoundError).
Replace the mock with a real JWKS document served from an in-process
node:http server, pointed at via the SDK's own endpoint overrides
(openIdMetadataUrl cloud override for the service validator, loginEndpoint
for the Entra factory). The v2-issuer acceptance test drives the underlying
JwtValidator with a decoupled jwksUriOptions {type:'uri'} so issuer
validation uses the real login host while keys come from the local server.
Deterministic, no external network, and exercises the real fetch + RS256
verify path.
* test(msteams): fix cloud-environment import and drop unused jwks-rsa
The extensions test-types gate resolves @microsoft/teams.api's dual
.d.ts/.d.mts types strictly and does not see the root re-export of
withOverrides/PUBLIC (TS2305), so import them from the canonical
auth/cloud-environment subpath the SDK itself uses. The JWKS-server rework
also removed the last direct jwks-rsa import, so drop the now-unused
devDependency and refresh the lockfile (knip deadcode).
---------
Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
Bot-generated files reuse names (openclaw runtime emits image-1.png every
generation), so Graph's default conflictBehavior=replace overwrote the prior
same-named driveItem in place. Because Teams caches file cards by driveItem
URL, channels kept showing the stale file and earlier content was lost from
OpenClawShared.
Force @microsoft.graph.conflictBehavior=rename on the simple upload so
SharePoint mints a uniquely-named driveItem (fresh name/eTag/URL) on
collision. The existing getDriveItemProperties re-read by item id already
surfaces the new name/URL to the Teams file card, so no downstream change is
needed.
Closes#112368
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@macos.shared>