* fix(mcp-runtime): use truncateUtf16Safe for MCP metadata text truncation
Replace naive .slice(0, N) with truncateUtf16Safe() to prevent
surrogate pair splitting in MCP tool metadata descriptions shown
to users through the bundle MCP runtime catalog.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(mcp-runtime): cover UTF-16 metadata boundary
* style(mcp-runtime): keep imports grouped
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(msteams): lowercase content type so attachment classification is case-insensitive
normalizeContentType only trimmed its input. MIME types are case-insensitive
(RFC 2045), and MS Teams relay payloads (SharePoint, OneDrive, Bot Framework CDN)
routinely emit mixed-case values such as "Image/PNG" or
"Application/Vnd.Microsoft.Teams.File.Download.Info". Every downstream
comparison in the attachments module assumes a lowercased value
(startsWith("image/"), === "application/vnd.microsoft.teams.file.download.info",
startsWith("text/html")), so a mixed-case attachment was silently misclassified:
images became documents, HTML bodies were skipped, and file-download candidates
were not resolved.
Lowercase in normalizeContentType so all six call sites match. The sibling
inferPlaceholder in the same file already lowercases via
normalizeLowercaseStringOrEmpty, so this aligns the two paths.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(msteams): route HTML text extraction through normalizeContentType
extractTextFromHtmlAttachments guarded its loop with an exact
`attachment.contentType !== "text/html"` check, which is the same
case-sensitivity gap the rest of the PR closes via normalizeContentType.
A mixed-case "TEXT/HTML" attachment (common from Teams relays; MIME types are
case-insensitive per RFC 2045) was skipped, so a message whose only body text
lived in such an HTML attachment entered the agent path with empty text.
Route the guard through normalizeContentType so mixed-case HTML attachments
reach the body extractor. Export the function and add regression coverage for
mixed-case / whitespace-padded / object-content / non-HTML cases.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(msteams): preserve MIME parameter case
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The Control UI sessions roster now leads with operator telemetry: overview
tiles (sessions / live / unread / tokens), per-kind row avatars with a live-run
dot, compact token values with context-usage meters (ok/warn/danger at
65%/85%), initial-load skeleton rows, and icon-led empty states. Stale token
snapshots render as ~approximations with a neutral meter and never drive
warning tones, matching the chat composer convention; the overview token sum
counts only known snapshots (~ when partial, n/a when none). Label chips
ellipsize instead of overflowing sticky columns; <=500px drops the avatar to
preserve the key column budget.
Closes#102517
* fix(agents): keep provider error detail truncation UTF-16 safe
Replace the raw detail.slice(0, limit - 1) in truncateErrorDetail with
truncateUtf16Safe so provider error previews do not emit lone surrogates
when an emoji falls on the truncation boundary.
Adds a regression test that places an emoji at the default truncation
boundary and asserts the formatted detail contains no lone surrogates.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(agents): prove provider error UTF-16 boundary
---------
Co-authored-by: chengzhichao-xydt <chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(talk): use truncateUtf16Safe for snippet text truncation
fast-context-runtime.ts normalizeSnippet and heartbeat-events-filter.ts
buildExecEventPrompt used raw .slice(0, N), which can produce lone
surrogates when truncation boundaries cross emoji surrogate pairs.
* test(talk): cover UTF-16 snippet boundary
---------
Co-authored-by: hailory <hailory@xydigit.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
The sidebar brand row (logo + wordmark) returned in 8295a12a83 but the
macOS-only titlebar mark from #100648 stayed, so the Mac app showed the
claw icon twice. Delete the sidebar-native-brand element and its
CSS gate; the sidebar brand row is the single brand surface and the
native titlebar strip holds only the traffic lights.
* fix(gateway): keep chat history display text truncation surrogate-safe
truncateChatHistoryText uses slice(0, N) to truncate chat history text
for the Control UI. When the truncation boundary falls inside a UTF-16
surrogate pair (emoji, CJK extended), the resulting string contains a
dangling surrogate that browsers render as U+FFFD (�).
Replace slice(0, maxChars) with truncateUtf16Safe(text, maxChars) so
the truncation point always falls on a complete code-point boundary.
* test(gateway): cover chat display UTF-16 boundary
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(tool-policy-audit): use truncateUtf16Safe for audit field truncation
Replace naive .slice(0, MAX) with truncateUtf16Safe() to prevent
surrogate pair splitting in tool policy audit log output.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(agents): cover audit UTF-16 boundary
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agents): keep tool-result context guard truncation UTF-16 safe
Replace the raw text.slice(0, cutPoint) in truncateTextToBudget with
truncateUtf16Safe so oversized tool results do not emit lone surrogates
when an emoji falls on the truncation boundary.
Adds a regression test that places an emoji exactly at the legacy cut
point and asserts the truncated output contains no lone surrogates.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(agents): report surrogate-safe omitted count
---------
Co-authored-by: chengzhichao-xydt <chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(matrix): truncate inbound preview on UTF-16 code-point boundary
The matrix inbound verbose preview used bodyText.slice(0, 200) before logging.
When a supplementary-plane character (emoji, extended CJK) straddles the 200th
code unit, the slice splits its surrogate pair and emits a lone surrogate into
the verbose log line. Lone surrogates corrupt JSON log serialization, break
terminal rendering, and crash UTF-8-validating log shippers.
Use the shared truncateUtf16Safe helper (already used by the mattermost sibling
monitor after #101630) to keep complete surrogate pairs intact. No behavior
change for ASCII input; the preview is still capped at 200 code units.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(matrix): fix inbound-preview test for lint and lib target
- Avoid String.prototype.isWellFormed (ES2024 lib): use the encodeURIComponent
well-formedness probe instead, which works on the project's TS lib target.
- Replace literal string concatenation with template strings to satisfy the
no-useless-concat lint rule. No behavior change in the assertions.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(channels): keep inbound log previews UTF-16 safe
* test(line): type verbose preview fixtures
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(stepfun): add step-3.7-flash model and make it default
* fix(stepfun): set step-3.7-flash cacheRead cost to 0.04
* fix(stepfun): raise step-3.7-flash maxTokens to context limit
Output was artificially capped at 65536; StepFun docs set max_tokens
default to INF, bounded only by the 256K context window. Raise
step-3.7-flash maxTokens to 262144 on both stepfun and stepfun-plan,
matching contextWindow. Per models.dev anomalyco/models.dev#1903.
step-3.5-flash entries unchanged.
* fix(stepfun): zero step-plan step-3.7-flash cost for plan billing
Step Plan endpoint bills per subscription plan, not per token, matching
the sibling stepfun-plan/step-3.5-flash and step-3.5-flash-2603 entries
which already carry zero cost. Standard stepfun/step-3.7-flash keeps its
per-token rate. Per models.dev anomalyco/models.dev#1903, whose step-plan
providers omit cost entirely.
* docs(changelog): note StepFun 3.7 support
* style(stepfun): format provider table
* fix(stepfun): forward reasoning effort
* fix(stepfun): scope reasoning compat to 3.7
* test(stepfun): keep 3.5 reasoning compat unchanged
* fix(stepfun): carry off fallback through agent turns
* fix(stepfun): use documented token limit field
* fix(stepfun): match chat completions contract
* chore(stepfun): keep changelog release-owned
---------
Co-authored-by: Tianning Li <litianning@stepfun.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>