* fix(compaction): use truncateUtf16Safe for post-compaction context text
Replace naive .slice(0, maxContextChars) with truncateUtf16Safe()
to prevent surrogate pair splitting in post-compaction context
summaries sent to the model.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(compaction): prove context UTF-16 boundary
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(config): use truncateUtf16Safe for allowed-values hint text
Replace naive .slice(0, N) with truncateUtf16Safe() to prevent
surrogate pair splitting in config validation hint messages
shown to users.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(config): prove UTF-16 allowed-value hint boundary
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* 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(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>
* 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>
* 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>
* fix(security): keep channel-metadata and install-policy truncation surrogate-safe
channel-metadata.ts and install-policy.ts both had private truncateText
functions using String.prototype.slice(0, N) on user-visible text.
channel-metadata is explicitly user-controlled untrusted content that gets
injected into LLM prompt context -- a dangling surrogate from unsafe
truncation would corrupt the prompt with invalid Unicode, affecting token
counting and potentially LLM response quality.
Replace .slice(0, ...) with truncateUtf16Safe(...) in both files so the
truncation point always falls on a complete code-point boundary.
Add test coverage for buildUntrustedChannelMetadata with emoji at both
the entry-level (400-char) and outer (800-char) truncation boundaries.
* test(security): prove UTF-16 truncation boundaries
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(acp): keep session update text truncation surrogate-safe
acp-projector's truncateText function used String.prototype.slice(0, N)
on session update text that is sent to AI providers in the prompt context.
When the truncation boundary falls inside a UTF-16 surrogate pair (emoji,
CJK extended), the resulting string contains a dangling surrogate that
can corrupt the prompt.
Replace .slice(0, maxChars-1) with truncateUtf16Safe(input, maxChars-1)
so the truncation point always falls on a complete code-point boundary.
* fix: make ACP projection truncation UTF-16 safe
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The bounded task label and progress summary shown for child ACP background runs used raw String.slice, which can split a surrogate pair at the 160/240-char boundaries and surface U+FFFD in requester-facing task status. Part of the UTF-16 safety sweep.
* fix(gateway): keep session title and preview text truncation UTF-16 safe
* test(gateway): cover session UTF-16 boundaries in place
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agents): keep steering metadata truncation UTF-16 safe
Replace raw string slicing with truncateUtf16Safe in promptLiteral so
emoji and CJK surrogate pairs in steering queue metadata are not split
mid-pair at the 500-char MAX_METADATA_CHARS cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(agents): add emoji surrogate boundary regression for steering metadata
Drive buildMergedAgentSteeringPrompt with a task label placing an emoji
at the 500-char MAX_METADATA_CHARS boundary to prove truncateUtf16Safe
preserves the surrogate pair instead of splitting it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(agents): assert steering truncation output
* test(agents): remove useless string concat in emoji boundary test
---------
Co-authored-by: Alix-007 <li.long15@xydigit.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: chengzhichao-xydt <chengzhichao-xydt@users.noreply.github.com>
* fix: keep emoji intact at remaining text truncation boundaries
Replace .slice(0, N) with truncateUtf16Safe() in five remaining
user-visible text truncation sites that were missed by earlier
UTF-16 safety sweeps:
- restart.ts: restart reason text (2 sites)
- chat-composer.ts: reply target text preview
- chat-thread.ts: thread text preview + message text extraction
All sites truncate user-generated content where emoji or other
supplementary-plane characters could produce lone surrogates.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: complete UTF-16-safe restart and chat boundaries
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Adds GitHub Enterprise data-residency support to the existing bundled GitHub Copilot provider.
Maintainer proof:
- GitHub CI green on head 54010a6538
- `check-lint`, `check-additional-extension-bundled`, and `check-shrinkwrap` passed in CI
- local `pnpm lint:extensions:bundled`, `pnpm lint`, and focused GitHub Copilot Vitest passed
- AWS Crabbox proof passed
- live microsoft.ghe.com device-flow/token-exchange/model-catalog proof passed
Co-authored-by: Tobias Oort <tobias.oort@ict.nl>
Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary:
- audit Doctor lint default selection after the full lint-family backfill
- make legacy state, skills readiness, session transcripts, and session snapshots explicit-only for default lint
- document default lint vs --all/--only and keep plugin/SDK public contracts unchanged
Validation:
- Galin review: no blocking findings
- maintainer accepted the default-lint compatibility tradeoff in PR comment
- exact-head hosted gates passed for d5d88a0db1: CI#28976811444 and Workflow Sanity#28976811343
- local broad pnpm check was blocked by a shrinkwrap guard failure that reproduces on origin/main and is unrelated to this five-file Doctor diff
Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com>