* feat(inbound-meta): expose message_type in trusted inbound metadata (fixes#50482)
Add resolveInboundMessageType() that extracts the media type prefix
(e.g. 'audio' from 'audio/ogg') from MediaType or MediaTypes fields.
Expose it as message_type in the inbound metadata JSON so agents can
distinguish voice messages from typed text for turn-completion heuristics.
* fix(inbound-meta): preserve per-turn source modality
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* feat(memory): apply outputDimensionality truncation to local GGUF embeddings
The outputDimensionality config field was passed through to the local
embedding provider but never applied. Local GGUF models (e.g.
Qwen3-Embedding-0.6B) always returned their full dimension vector.
Apply slice(0, N) after normalization so MRL-capable models can benefit
from dimension truncation — matching the behavior already supported by
Gemini embedding-2 and OpenAI providers.
Fixes#58765
* fix(memory): preserve local embedding dimensions through worker
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
The isToolDocBlockStart function checked normalized === normalized.toUpperCase()
but normalized is already uppercased from line 24, making the condition always true.
This caused mixed-case lines ending with ':' to be incorrectly detected as doc block
starts, truncating tool descriptions unnecessarily.
Compare the original line instead to correctly detect all-uppercase headings.
Co-authored-by: Gautam Kumar <gautamkumarofficial@users.noreply.github.com>
* fix(usage): reject invalid explicit dates in usage RPC date parsing
usage.cost and sessions.usage accepted shape-valid but impossible dates such as 2026-02-30: parseDateParts validated only the YYYY-MM-DD regex, so Date.* silently rolled them over (2026-02-30 -> 2026-03-02) and the RPC returned cost/usage for the wrong day. Out-of-range parts now fail a UTC round-trip check, and an explicitly provided unparseable date (bad format or impossible calendar date) returns INVALID_REQUEST instead of silently falling back to the default range. Absent/valid dates are unchanged.
[AI-assisted]
* fix(usage): reject non-string explicit dates
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Duplicate user-message detection ran over the full branch, so when a prompt
was re-sent within the 60s window its earlier copy in the summarized prefix
and the later copy in the kept tail were both removed: the summarized copy via
summarizedBranchIds and the kept copy as a duplicate. With
truncateAfterCompaction enabled the prompt then vanished from the successor
transcript entirely. Restrict dedup to the kept region so the first surviving
copy is preserved.
* fix: pin plugin workspace dir for sessions.list to avoid O(rows) memo busting
sessions.list was O(rows) slow under concurrent agent/cron load because
each row read a process-global active plugin-registry workspace dir
that was mutated by other turns between rows. The per-row memo key
changed every time, so loadPluginMetadataSnapshot scanned fresh
(~100ms per row).
Fix:
1. Add AsyncLocalStorage-based workspace dir pinning to
runtime-workspace-state.ts — withPinnedActivePluginRegistryWorkspaceDir()
snapshots the current workspace dir for the duration of a callback.
2. Wrap listSessionsFromStoreAsync body in the pin so all per-row
metadata lookups use a stable memo key.
Fixes#90814
* test(plugins): cover request-scoped workspace pins
* fix(plugins): pin canonical runtime workspace reads
* fix(plugins): preserve workspace pins across reloads
---------
Co-authored-by: lsr911 <lsr911@github.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(ollama): preserve configured API during discovery
* fix(ollama): keep compatible discovery base URL
* fix(ollama): route compatible APIs through configured transport
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix: scope assistant avatar override to agent ID
The local assistant avatar override was stored globally in
localStorage without an agentId, causing the same avatar to
apply to all agents. Setting an avatar for agent A would
overwrite the avatar for agent B.
Fix: include agentId when saving the local avatar override,
and filter by agentId when loading. An override saved for one
agent no longer bleeds into other agents.
Fixes#90890
* fix(ui): persist assistant avatars per agent
* fix(ui): satisfy scoped avatar checks
---------
Co-authored-by: lsr911 <lsr911@github.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
The 'can preserve asynchronous provider model discovery' test was
flaky because resolveModelAsyncMock in beforeEach delegates to
resolveModelMock. When useAsyncModelResolution=true, the test
asserted resolveModelMock was not called, but the delegation
caused it to be called, failing CI on two lanes.
Fix: use a standalone vi.fn() for the async resolver in this
test, and explicitly reset resolveModelMock before the assertion
to guard against mock state leakage from prior tests.
Fixes#92117
Co-authored-by: lsr911 <lsr911@github.com>
MiniMax TTS API returns HTTP 200 even on quota/billing errors, with the
error encoded in base_resp.status_code. Without this check, placeholder
audio returned alongside the error is silently accepted, preventing the
TTS dispatcher from falling back to a configured secondary provider.
This follows the same pattern used by all other MiniMax providers:
- image-generation-provider.ts
- video-generation-provider.ts
- music-generation-provider.ts
- minimax-web-search-provider.runtime.ts
Fixes#76904
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>