* fix(discord): use configured statusReactions.timing instead of DEFAULT_TIMING
Makes the Discord message finalizer respect user-configured
doneHoldMs/errorHoldMs instead of always using hardcoded
DEFAULT_TIMING values. Brings Discord to parity with Telegram
and Slack which already use configured timing.
Fixes#78431
* test(discord): strengthen configured-timing regression with old-default assertion
Add a no-cleanup assertion at DEFAULT_TIMING.doneHoldMs (1500ms) before
advancing to the configured 2000ms hold, so the test fails against the
old hardcoded-default behavior. Add a matching errorHoldMs regression
test using failedCounts to drive the error terminal path.
Address ClawSweeper [P2] review finding on #94736: the previous test
advanced straight to 2000ms and would pass without the runtime fix
because main already removes the done reaction at 1500ms.
* refactor(discord): unify status reaction timing proof
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(discord): handle ffmpeg stderr stream errors in voice playback
createDiscordOpusPlaybackStream guards ffmpeg stdout and stdin against raw
stream 'error' events (an unhandled stream error throws and crashes the
gateway via uncaughtException), but the stderr stream on the same child was
left unguarded. Add the symmetric stderr error handler, routing it through
opusStream.destroy like stdout. Add a regression test covering stdout and
stderr stream errors.
* refactor(discord): unify ffmpeg stream errors
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(memory-core): clear daily-ingestion sqlite namespace on dreaming repair
repairDreamingArtifacts() cleared the dreaming-session-ingestion-files and
dreaming-session-ingestion-seen namespaces but not the migrated
dreaming-daily-ingestion namespace. After #92020 taught auditDreamingArtifacts()
to treat all three ingestion namespaces as ingestion state, the repair path
became asymmetric: the memory status --fix re-audit still reported
sessionIngestionExists=true from the surviving daily rows, and the daily
ingestion bookkeeping leaked past repair so daily memory files were not
re-ingested on the next sweep.
Clear DREAMING_DAILY_INGESTION_NAMESPACE alongside the session files/seen
namespaces so repair fully resets dreaming ingestion state, matching the audit.
* chore: retrigger CI for real behavior proof check
* fix(memory-core): keep daily ingestion outside session repair
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Alix-007 <li.long15@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* [AI] fix(agents): classify HTTP 429 overloaded bodies as overloaded, not rate_limit
Reorder overloaded check before rate_limit across all three classification paths:
- classifyFailoverClassificationFromHttpStatus (HTTP status path)
- classifyFailoverClassificationFromMessage (message text path)
- formatRateLimitOrOverloadedErrorCopy (user-facing copy path)
Previously, isRateLimitErrorMessage matched the bare '429' token in
overloaded-worded 429 bodies (e.g. z.ai code 1305), misclassifying them
as rate_limit and showing the wrong user-facing error message. 503/499
already check overloaded before defaulting; 429 now follows the same pattern.
Related to #98101
* [AI] fix(agents): put MODEL_CAPACITY_ERROR_RE before overloaded in user-facing copy
MODEL_CAPACITY_ERROR_RE is the most specific overloaded variant
('selected model is at capacity') and must be checked before the
broader isOverloadedErrorMessage matcher (which also covers model at
capacity), otherwise it's shadowed with a less specific message.
Related to #98101
* fix(agents): preserve overload copy for rate-limited responses
* fix(agents): retain provider retry guidance
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(feishu): avoid forwarding mentions without bot open id
* fix(feishu): require bot identity for mention forwarding
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(auth): preserve auth profile order when copying store to spawned agent
* fix(auth): preserve copied profile order
Co-authored-by: Vivek Behani <vivekbehani@me.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(openai): stop double-prefixing SSE bodies mislabeled as JSON
OpenAI-compatible gateways that stream real SSE (data: {...}) but label the
response application/json hit the streaming JSON-wrap fallback, which re-prefixed
each frame as 'data: data: {...}' and broke JSON.parse in the OpenAI SDK. Sniff
JSON-labeled streaming bodies and relabel genuine SSE as text/event-stream so the
SSE sanitizer parses them verbatim.
* test(openai): prove mislabeled SSE stays streaming
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(crestodian): suppress unhandled stdout/stderr stream errors in probeLocalCommand
Add no-op error listeners on child stdout/stderr in probeLocalCommand
to prevent unhandled EventEmitter errors from crashing the process.
Replace standalone proof scripts with Vitest regression test that verifies
error listeners are registered on the production code path.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(crestodian): exercise probe stream failures
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ui): redesign sessions page into roster table with details drawer
* fix(ui): decouple sessions drawer expansion from deep-link query narrowing; update mobile column hiding for new roster
* fix(ui): reload open session drawer when a session gains compaction checkpoints