Commit Graph

14508 Commits

Author SHA1 Message Date
Jayesh Betala
8f31b3218f Fix Discord gzip response parsing (#80788)
* Fix Discord gzip response parsing

* fix(discord): decode gzipped REST responses

* fix(clownfish): address review for live-pr-repair-20260628T212840-001 (1)

---------

Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-28 14:43:28 -07:00
NIO
91297bf420 fix(google): bound embedding-batch JSON response reads (#97535)
* fix(google): bound embedding-batch JSON response reads

* fix(google): repair embedding-batch test types and lint

---------

Co-authored-by: NIO <nocodet@mail.com>
2026-06-28 14:20:28 -07:00
xingzhou
cc757839d5 fix: proxy streams fail fast on oversized or stalled responses (#97235)
* fix proxy response and SSE read bounds

* test(line): reuse surrogate regex in action tests

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-28 13:04:47 -07:00
黄伟浩
f4201578e5 fix(matrix): warn on accumulated token storage roots (#97353) 2026-06-28 12:34:52 -07:00
NIO
b948c86b2a fix(telegram): bound getChat Bot API response reads (#97274)
* fix(telegram): bound getChat Bot API response reads

* fix(telegram): repair getChat bound-read type and proof script lint

---------

Co-authored-by: NIO <nocodet@mail.com>
2026-06-28 12:29:14 -07:00
NIO
a07d59e014 fix(telegram): bound Bot API response reads to prevent OOM (#97271)
* fix(telegram): bound Bot API response reads to prevent OOM

* fix(scripts): make proof-telegram-bound.mjs lint-clean

---------

Co-authored-by: NIO <nocodet@mail.com>
2026-06-28 12:28:51 -07:00
LiLan0125
a083c76621 fix(bedrock): honor adaptive model max tokens (#97343) 2026-06-28 12:15:02 -07:00
Masato Hoshino
c026546063 fix(signal): sanitize internal tool-trace lines from outbound text (#97360)
Wrap the signal outbound sanitizeText hook with sanitizeAssistantVisibleText so assistant internal tool-trace scaffolding is stripped before delivery, matching the sibling channel fixes under #90684 (Telegram #95774, Google Chat #95084, IRC #97214).
2026-06-28 12:14:45 -07:00
Masato Hoshino
cd6d0f9b00 fix(slack): sanitize internal tool-trace lines from outbound text (#97367)
Wrap the slack outbound sanitizeText hook with sanitizeAssistantVisibleText so assistant internal tool-trace scaffolding is stripped before delivery, matching the sibling channel fixes under #90684 (Telegram #95774, Google Chat #95084, IRC #97214).
2026-06-28 12:14:35 -07:00
ly-wang19
20645753a4 fix(feishu): truncate streaming-card summary on a code-point boundary (#97462)
truncateSummary used clean.slice(0, max - 3), which can cut between the
two UTF-16 halves of a surrogate pair (emoji / astral char) straddling
the limit. The serialized card summary then carries a lone high
surrogate that Feishu renders as the replacement char.

Slice with the surrogate-safe sliceUtf16Safe helper instead, matching
the pattern already used in extensions/slack/src/truncate.ts, so a
straddling code point is dropped whole.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 12:10:42 -07:00
ly-wang19
3e64d84712 fix(qqbot): treat escaped pipes as literal content when splitting table cells (#97429)
splitTableCells/splitPartialTableCells split on every '|', including a GFM
backslash-escaped pipe ('\|'), which is literal cell content rather than a
column delimiter. A cell containing '\|' was therefore mis-counted as multiple
columns, so the oversized-row fallback (renderTableRowAsFields) rendered the
trailing content under the wrong header.

Split via an escape-aware scan that treats '\|' as a literal '|' (and '\\' as
a literal backslash so a following '|' still delimits). Behavior is byte-for-byte
unchanged for any row without an escaped pipe, so existing chunking is preserved.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 12:03:52 -07:00
Omar Shahine
9b9a124cc5 Fix Codex message-tool-only source reply completion (#95942)
* fix(codex): recognize message tool source replies

* fix(codex): accept numeric source message ids

* fix(codex): account for source reply SDK surface

* fix(codex): require delivered reply receipts

* fix(codex): reject alias-routed source replies

* fix(codex): require delivered non-send telemetry

* fix(codex): honor normalized source routes

---------

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
2026-06-28 12:02:36 -07:00
ly-wang19
4b36cf451c fix(line): truncate template title/altText on grapheme boundaries, not raw UTF-16 (#97428)
* fix(line): truncate template title/altText on grapheme boundaries, not raw UTF-16

createConfirmTemplate/createButtonTemplate/createTemplateCarousel/createCarouselColumn/
createImageCarousel truncated title and altText with a raw `.slice(0, N)`, so an
emoji straddling a LINE field limit (e.g. a 40-char button title) was cut in half,
leaving a lone high surrogate that LINE renders as the replacement char or rejects.
Route those fields through the file's existing grapheme-safe truncateTemplateText
(already used for the text body) via a small truncateOptionalTemplateText wrapper.
Byte-identical for all-BMP input; only straddling-emoji truncation changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: retry OpenGrep scan (HTTP 502 infra flake)

* test(line): cover grapheme-safe template fields

---------

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-28 11:59:59 -07:00
ly-wang19
630034ef62 fix(memory-wiki): keep claim freshness tied to evidence timestamps (#97465)
* fix(memory-wiki): keep claim freshness tied to evidence

* fix(memory-wiki): preserve page freshness fallback only for untimestamped claims

---------

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
2026-06-28 11:58:45 -07:00
Masato Hoshino
25490d4c42 fix(matrix): sanitize internal tool-trace lines from outbound text (#97372)
Wrap the matrix outbound sanitizeText hook with sanitizeAssistantVisibleText so assistant internal tool-trace scaffolding is stripped before delivery, matching the sibling channel fixes under #90684 (Telegram #95774, Google Chat #95084, IRC #97214).
2026-06-28 11:58:36 -07:00
qingminlong
5e1f4c1073 fix(memory): stop light dreaming from restaging stale summaries (#97446)
* fix(memory): stop restaging stale light dreams

* fix(memory): preserve cross-day light freshness
2026-06-28 11:58:24 -07:00
NIO
4c477ee632 fix(openai): bound embedding-batch and realtime session JSON response reads (#97533) 2026-06-28 11:53:50 -07:00
NIO
51064bda4d fix(signal): bound GitHub release info JSON response with readProviderJsonResponse (#97536)
Replace bare `await response.json()` in `installSignalCliFromRelease` with
`readProviderJsonResponse` (16 MiB cap, stream cancel on overflow). The
external GitHub Releases endpoint can include a large `body` changelog field;
the error path was already guarded but the success path was unbounded.
The existing inner catch continues to convert overflow errors into the
graceful `{ ok: false, error: "Failed to parse signal-cli release info." }` path.

Adds a regression test verifying the stream is cancelled before all chunks are
read on an oversized 20 MiB streaming response.

Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 11:46:25 -07:00
Jimmy Puckett
71347ef999 fix(msteams): handle message card submit values (#97546) 2026-06-28 11:46:16 -07:00
ly-wang19
eff68d2c77 fix(line): truncate action fields on code-point boundaries
Use surrogate-safe truncation for LINE action labels/data, including card-command, markdown link, quick-reply, and media-control action surfaces, with focused regression coverage.
2026-06-28 11:37:51 -07:00
ly-wang19
881ec2f93f fix(mattermost): truncate draft previews on code-point boundaries (#97472)
Summary:
- The PR replaces Mattermost draft preview raw UTF-16 slicing with the existing SDK `sliceUtf16Safe` helper and adds a regression test for an emoji that straddles the preview limit.
- PR surface: Source +1, Tests +26. Total +27 across 2 files.
- Reproducibility: yes. Source inspection shows current main raw-slices at `maxChars - 3`; driving `createMatt ...  at UTF-16 indices 8-9 reaches the lone-surrogate path, though I did not run tests in this read-only sweep.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(mattermost): truncate draft previews on code-point boundaries

Validation:
- ClawSweeper review passed for head 86d0dd2a06.
- Required merge gates passed before the squash merge.

Prepared head SHA: 86d0dd2a06
Review: https://github.com/openclaw/openclaw/pull/97472#issuecomment-4825788514

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
2026-06-28 18:20:33 +00:00
mushuiyu886
45f261ff7a fix(memory-wiki): truncate import insights safely
Use the shared UTF-16 safe truncation helper for Memory Wiki import insight summaries and add regression coverage for surrogate-boundary summaries.
2026-06-28 11:19:55 -07:00
Alix-007
48f34b1d4d fix(openrouter): bound video response reads
Route OpenRouter video submit and poll success JSON through the shared bounded provider JSON reader, preserving malformed-response mapping and SSRF request policy coverage.
2026-06-28 11:19:47 -07:00
ly-wang19
55d7b5b36c fix(matrix): truncate reply context on code-point boundaries (#97471)
truncateReplyBody used a raw value.slice(0, MAX_REPLY_BODY_LENGTH - 3)
to shorten long reply bodies. When the cut index fell between the two
UTF-16 code units of a surrogate pair (e.g. an emoji), the slice left a
lone high surrogate before the ellipsis, which renders as a broken glyph
in the reply context shown to the agent.

Replace the raw slice with sliceUtf16Safe from the plugin SDK so the
truncation never cuts inside a surrogate pair. A normal (non-astral)
body is unaffected.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:06:36 -07:00
wendy
6ce88ca51d fix(irc): prevent ghost nick collisions on rejoin after network delay (#96108)
* fix(irc): prevent ghost nick collisions on rejoin after network delay

* test(irc): add regression tests for fallback nick uniqueness

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: wendy-chsy <wan.wenyan@xydigit.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-28 10:47:39 -07:00
NIO
c0883a531d fix(openrouter): bound generation-cost JSON response reads (#97490) 2026-06-28 10:44:36 -07:00
NIO
0d59280131 fix(deepinfra): bound video generation JSON response reads (#97486) 2026-06-28 10:44:28 -07:00
mushuiyu886
891096926e fix(opencode): restore Zen model catalog (#92495)
* fix(opencode): restore Zen model catalog

* fix(opencode): restore Zen transport routing

* fix(opencode): broaden Zen fallback catalog

* fix(opencode): correct Zen family routing

* fix(opencode): route Zen MiniMax through Anthropic

* fix(opencode): filter Zen live-only catalog rows

* fix(opencode): route MiniMax through Zen chat completions

* fix(opencode): omit unverified Zen model costs

* fix(opencode): align sampled Zen costs

* fix(opencode): keep Zen cost metadata required

* fix(opencode): keep Zen docs examples resolvable

* fix(opencode): move Zen catalog to provider discovery

* test(opencode): cover Zen discovery cache isolation

* fix(opencode): add Zen GLM-5.2 catalog coverage

* test(opencode): detect Zen catalog drift

Signed-off-by: sallyom <somalley@redhat.com>

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
2026-06-28 12:32:44 -04:00
Alix-007
2f851ecfe9 fix(speech): bound TTS response reads (#96874) 2026-06-28 10:57:25 -04:00
Alix-007
25e184aeab fix(minimax): bound video control response reads (#96889)
* fix(minimax): bound image/video success response reads

MiniMax image generation and video generation (task submit + status poll)
read their success responses through unbounded `await response.json()`, so
a misbehaving or hostile endpoint could stream an arbitrarily large body
into memory before parsing and exhaust the process. Read those success
bodies through the shared bounded reader (16 MiB cap, the same limit other
bundled providers and the sibling MiniMax web-search provider already use)
and cancel the stream on overflow. The error-body path is already bounded
via assertOkOrThrowHttpError; this closes the matching success-JSON gap.
MiniMax TTS is already bounded and is left unchanged.

AI-assisted.

* fix(minimax): bound video metadata response reads

* fix(minimax): leave image response sizing to image hardening

* fix(minimax): bound image/video success response reads

MiniMax image generation and video generation (task submit + status poll)
read their success responses through unbounded `await response.json()`, so
a misbehaving or hostile endpoint could stream an arbitrarily large body
into memory before parsing and exhaust the process. Read those success
bodies through the shared bounded reader (16 MiB cap, the same limit other
bundled providers and the sibling MiniMax web-search provider already use)
and cancel the stream on overflow. The error-body path is already bounded
via assertOkOrThrowHttpError; this closes the matching success-JSON gap.
MiniMax TTS is already bounded and is left unchanged.

AI-assisted.

* fix(minimax): bound video metadata response reads
2026-06-28 10:52:33 -04:00
Alix-007
9241b9701d fix(mattermost): bound successful REST JSON/text response reads (#96033)
* fix(mattermost): bound successful REST JSON/text response reads

The Mattermost REST client already bounds error bodies
(readResponseTextLimited) and streams guarded responses without buffering,
but the success path still called `await res.json()` / `await res.text()`,
reading the whole body into memory before parsing. A self-hosted or
compromised Mattermost server can return an arbitrarily large (or
never-terminating, content-length-less) JSON/text body and force the plugin
to buffer it unbounded.

Read successful JSON through the shared readProviderJsonResponse (16 MiB cap,
cancels the stream and throws a bounded error on overflow, same as the
provider HTTP path) and cap non-JSON success bodies with readResponseTextLimited.
uploadMattermostFile's file-info JSON is bounded the same way.

Symmetric follow-up to the #95103 / #95108 response-limit campaign.

AI-assisted.

* fix(mattermost): bound probe success JSON reads

* fix(mattermost): reject oversized success text bodies
2026-06-28 10:40:32 -04:00
Alix-007
d577cb2fe9 fix(nextcloud-talk): bound external send/reaction response reads to prevent OOM (#96031)
* fix(nextcloud-talk): bound external send/reaction response reads to prevent OOM

Nextcloud Talk talks to self-hosted servers whose HTTP responses are not
trusted to be small. The send and reaction paths buffered three external
bodies without any byte cap:

- success JSON via await response.json()
- send error text via await response.text()
- reaction error text via await response.text()

A hostile or misbehaving Nextcloud endpoint could stream an unbounded body
(no content-length) into memory, pressuring or hanging the plugin/provider
path. Cap success JSON at 16 MiB via readResponseWithLimit and collapse error
bodies to an 8 KiB readResponseTextSnippet, cancelling the stream on overflow.
The 'message sent but receipt JSON unreadable -> unknown' fallback is
preserved (an over-limit body now also routes through the existing catch).

This is the symmetric counterpart to the #95103/#95108 response-limit
campaign, reusing the shared @openclaw/media-core helpers (newly re-exported
from plugin-sdk/response-limit-runtime for plugin consumers).

* fix(nextcloud-talk): bound error bodies via public readResponseTextLimited (no new plugin-SDK surface)

Re-exporting readResponseTextSnippet from plugin-sdk/response-limit-runtime
pushed the public plugin-SDK export count past its surface budget, failing
plugin-sdk-surface-report.test.ts. Drop that re-export and instead bound the
Nextcloud Talk send/reaction error bodies through the already-public
readResponseTextLimited (openclaw/plugin-sdk/provider-http), collapsing the
bounded 8 KiB prefix to a short, log-safe snippet locally. Behavior is
unchanged for callers; no new plugin-SDK surface is introduced.

Success JSON still reads through readResponseWithLimit (16 MiB cap). The
committed bounded-response-reads Vitest suite continues to prove the caps
hold against 17 MiB streamed bodies with no content-length.

* fix(nextcloud-talk): reuse shared readProviderJsonResponse for send success JSON

The send success receipt parsed JSON by hand via readResponseWithLimit + a
local NEXTCLOUD_TALK_JSON_MAX_BYTES cap + JSON.parse(TextDecoder.decode(...)),
duplicating the shared provider-http helper that the sibling room-info.ts and
bot-preflight.ts already use. extensions/AGENTS.md forbids re-implementing
shared helpers locally.

Swap the hand-rolled block for the one-stop
readProviderJsonResponse<{ ocs?: ... }>(response, "Nextcloud Talk send"), which
reads through the same bounded reader and throws on overflow/malformed JSON, so
the outer try/catch still keeps the "unknown" receipt and behavior is
equivalent. The error path keeps readResponseTextLimited (text, not JSON).
2026-06-28 10:30:17 -04:00
Alix-007
2d2a50c00d fix(discord): bound REST response body to prevent OOM flood (#95412)
The Discord REST main response path read the body with an unbounded
await response.text() before JSON-parsing it. A controlled or hijacked
endpoint could stream an arbitrarily large body and exhaust memory (OOM).

Wrap the read in the canonical readResponseWithLimit helper with an 8 MiB
cap (well above any legitimate Discord JSON payload) plus an idle timeout
tied to the request timeout, so the stream is cancelled at the cap or on
stall instead of buffering unbounded. Normal payloads still parse fully.

This mirrors PR #95108 which bounded the analogous Anthropic Messages
error-response read with the same helper.
2026-06-28 10:20:37 -04:00
NIO
38ddcef78f Fix/zalo bound api json response reads (#97277)
* fix(zalo): bound Bot API JSON response reads via readProviderJsonResponse

* test(zalo): keep API proof in focused coverage

---------

Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-06-28 02:04:22 -07:00
NIO
7ba9212665 Fix/discord bound probe getme json reads (#97278)
* fix(discord): bound probe getMe JSON response reads

* test(discord): add oversized probe getMe JSON regression

* test(discord): add loopback proof for bounded probe getMe reads

* fix(scripts): satisfy oxlint in discord probe proof script

* test(discord): keep probe proof in focused coverage

---------

Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-06-28 01:57:20 -07:00
Bek
9c95abd49d fix: seed Slack thread context after reset (#97100) 2026-06-28 02:36:53 -04:00
Dallin Romney
119dc4bd82 test: promote OpenAI HTTP QA coverage (#97369) 2026-06-27 22:28:31 -07:00
Andy Ye
830467bc93 fix(imessage): stage remote media for plugin claims (#91803) 2026-06-27 19:36:21 -07:00
Ayaan Zaidi
c52adf7505 test(telegram): cover retained preview chunk gaps 2026-06-27 12:30:57 -07:00
Ayaan Zaidi
199700de26 fix(telegram): replay retained preview gaps 2026-06-27 12:30:57 -07:00
llagy009
2720ac06b7 fix(duckduckgo): guard out-of-range numeric HTML entities (#96583)
decodeHtmlEntities decoded numeric entities with String.fromCodePoint(parseInt(...)) without a range check, so an out-of-range entity such as &#99999999; or &#x110000; threw RangeError and made the whole results page fail to parse. Validate the code point is within 0..0x10FFFF and keep the original entity text otherwise. Add a regression covering decimal and hex out-of-range entities plus a valid astral entity.
2026-06-27 09:37:58 -07:00
miorbnli
ce15f348bb fix(telegram): use idempotent retry context for delete/reaction (#96612)
reactMessageTelegram and deleteMessageTelegram passed context: "send" to
isRecoverableTelegramNetworkError, which disables message-snippet matching
(allowMessageMatch defaults to false only for "send"). Both operations are
idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a
transient snippet-only network error (e.g. "socket hang up", "undici network
error" with no error code) was not retried — stricter than polling/webhook/
unknown, which all default allowMessageMatch to true. Users saw spurious
reaction/delete failures on transient network errors.

Add delete | react to TelegramNetworkErrorContext (additive) and use them at
the two callers. The helper default (context !== "send") is unchanged, so
delete/react now match polling/webhook/unknown. sendMessage keeps "send".

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-27 09:31:52 -07:00
llagy009
e5c3c59c67 fix(synology-chat): truncate sanitized input on UTF-16 boundary (#96574)
sanitizeInput truncated long messages with String.slice(0, 4000), which
can cut through an astral character's surrogate pair (e.g. an emoji at
the 4000-char boundary), leaving a lone surrogate in the sanitized text
passed downstream.

Use truncateUtf16Safe so truncation never splits a surrogate pair,
keeping the existing 4000-char budget and '... [truncated]' suffix.

Adds tests asserting the truncated output stays UTF-16 well formed and
that a supplementary-plane character is preserved when it fits.
2026-06-27 09:31:33 -07:00
llagy009
2e881ab1c6 fix(googlechat): truncate approval card text on UTF-16 boundary (#96573)
truncateText sliced the approval card text paragraph with String.slice,
which can cut through an astral character's surrogate pair (e.g. an emoji
straddling the 1797-char limit), leaving a lone surrogate in the card
text sent to Google Chat.

Use truncateUtf16Safe from the plugin SDK so truncation never splits a
surrogate pair, keeping the '...' suffix and the existing length budget.

Adds tests asserting the truncated Command card text stays UTF-16 well
formed and that an astral character is preserved when it fits.
2026-06-27 09:31:26 -07:00
llagy009
90c20d15c2 fix(slack): truncate approval mrkdwn on UTF-16 boundary (#96576)
truncateSlackMrkdwn cut approval Block Kit mrkdwn (Command/Request/
plugin description) with String.slice(0, maxChars - 1), which can split
an astral character's surrogate pair at the 2600-char preview limit,
leaving a lone surrogate in the chat.postMessage/chat.update payload.

Slice with sliceUtf16Safe so truncation never splits a surrogate pair,
keeping the existing ellipsis suffix and length budget.

Adds tests asserting exec command and plugin request mrkdwn stay free of
lone surrogates, plus a BMP regression keeping the existing limit.
2026-06-27 09:31:20 -07:00
llagy009
cb8bc71ff8 fix(whatsapp): elide auto-reply text on UTF-16 boundary (#96580)
elide truncated text with String.slice(0, limit) on a UTF-16 code-unit
index, so an astral character straddling the limit was cut into a lone
surrogate; the truncated-char count was also computed from the fixed
limit rather than the actual kept length.

Truncate with truncateUtf16Safe so a surrogate pair is never split, and
derive the truncated-char count from the kept length so the annotation
stays accurate.

Adds tests asserting no lone surrogate when the limit lands inside an
emoji and that a complete astral character is kept when it fits.
2026-06-27 09:31:16 -07:00
llagy009
b5c662f4f5 fix(codex): keep CLI session preview text on code-point boundaries (#96582)
truncateText shortened the cached lastMessage preview with value.slice(0, max - 3), which can cut a surrogate pair in half and emit a lone surrogate into the codex CLI session list JSON. Use the shared truncateUtf16Safe helper so truncation falls back to a whole code-point boundary. Add regressions for both the history.jsonl and sessions/**/*.jsonl preview paths.
2026-06-27 09:31:00 -07:00
llagy009
d693ed4af3 fix(qqbot): truncate reminder job name on code-point boundary (#96575)
generateJobName truncated reminder content with String.slice(0, 20) on
a UTF-16 code-unit index, so an astral character (e.g. an emoji) landing
on the boundary was cut into a lone surrogate, producing a malformed
cron job name.

Truncate with the shared truncateUtf16Safe helper so a surrogate pair is
never split, keeping the existing 20-unit budget and ellipsis suffix.

Adds a test asserting the truncated job name contains no lone surrogate.
2026-06-27 09:30:52 -07:00
llagy009
6c5a9fde9f fix(msteams): truncate reflection prompt on UTF-16 boundary (#96578)
buildReflectionPrompt truncated the thumbed-down response with
String.slice(0, 500) on a UTF-16 code-unit index, so an astral
character straddling the 500-char cap was cut into a lone surrogate in
the reflection prompt built for the LLM.

Use truncateUtf16Safe so truncation never splits a surrogate pair,
keeping the existing 500-char budget and '...' suffix.

Adds tests asserting the prompt stays UTF-16 well formed when truncating
and that a boundary emoji is preserved when it fits.
2026-06-27 09:30:26 -07:00
Vincent Koc
b8e3de1160 fix(telegram): recover stalled ingress spool claims (#97118)
* fix(telegram): drain ingress with native queue claims

* fix(telegram): bound native claim drain snapshots

* fix(telegram): recover pid-reused ingress claims

* fix(channels): block claimed candidate lanes

* fix(telegram): recover stalled ingress spool claims

* test(telegram): cover native claimNext drain stalls

---------

Co-authored-by: Dallin Romney <dallinromney@gmail.com>
2026-06-27 09:14:14 -07:00