* feat(gateway): implement claim check pattern to prevent OOM on large attachments
* fix: sanitize mediaId, refine trimEnd, remove warn log, add threshold and absolute path
* fix: enforce maxBytes before decoding and use dynamic path from saveMediaBuffer
* fix: enforce absolute maxBytes limit before Buffer allocation and preserve file extensions
* fix: align saveMediaBuffer arguments and satisfy oxfmt linter
* chore: strictly enforce linting rules (curly braces, unused vars, and error typing)
* fix: restrict offload to mainstream mimes to avoid extension-loss bug in store.ts for BMP/TIFF
* fix: restrict offload to mainstream mimes to bypass store.ts extension-loss bug
* chore: document bmp/tiff exclusion from offload whitelist in MIME_TO_EXT
* feat: implement agent-side resolver for opaque media URIs and finalize contract
* fix: support unicode media URIs and allow consecutive dots in safe IDs based on Codex review
* fix(gateway): enforce strict fail-fast for oversized media to prevent OOM bypass
* refactor(gateway): harden media offload with performance and security optimizations
This update refines the Claim Check pattern with industrial-grade guards:
- Performance: Implemented sampled Base64 validation for large payloads (>4KB) to prevent event loop blocking.
- Security: Added null-byte (\u0000) detection and reinforced path traversal guards.
- I18n: Updated media-uri regex to a blacklist-based character class for Unicode/Chinese filename support, with oxlint bypass for intentional control regex.
- Robustness: Enhanced error diagnostics with JSON-serialized IDs.
* fix: add HEIC/HEIF to offload allowlist and pass maxBytes to saveMediaBuffer
* fix(gateway): clean up offloaded media files on attachment parse failure
Address Codex review feedback: track saved media IDs and implement best-effort cleanup via deleteMediaBuffer if subsequent attachments fail validation, preventing orphaned files on disk.
* fix(gateway): enforce full base64 validation to prevent whitespace padding bypass
Address Codex review feedback: remove early return in isValidBase64 so padded payloads cannot bypass offload thresholds and reintroduce memory pressure. Updated related comments.
* fix(gateway): preserve offloaded media metadata and fix validation error mapping
Address Codex review feedback:
- Add \offloadedRefs\ to \ParsedMessageWithImages\ to expose structured metadata for offloaded attachments, preventing transcript media loss.
- Move \erifyDecodedSize\ outside the storage try-catch block to correctly surface client base64 validation failures as 4xx errors instead of 5xx \MediaOffloadError\.
- Add JSDoc TODOs indicating that upstream callers (chat.ts, agent.ts, server-node-events.ts) must explicitly pass the \supportsImages\ flag.
* fix(agents): explicitly allow media store dir when loading offloaded images
Address Codex review feedback: Pass getMediaDir() to loadWebMedia's localRoots for media-uri refs to prevent legacy path resolution mismatches from silently dropping large attachments.
* fix(gateway): resolve attachment offload regressions and error mapping
Address Codex review feedback:
- Pass \supportsImages\ dynamically in \chat.ts\ and \gent.ts\ based on model catalog, and explicitly in \server-node-events.ts\.
- Persist \offloadedRefs\ into the transcript pipeline in \chat.ts\ to preserve media metadata for >2MB attachments.
- Correctly map \MediaOffloadError\ to 5xx (UNAVAILABLE) to differentiate server storage faults from 4xx client validation errors.
* fix(gateway): dynamically compute supportsImages for overrides and node events
Address follow-up Codex review feedback:
- Use effective model (including overrides) to compute \supportsImages\ in \gent.ts\.
- Move session load earlier in \server-node-events.ts\ to dynamically compute \supportsImages\ rather than hardcoding true.
* fix(gateway): resolve capability edge cases reported by codex
Address final Codex edge cases:
- Refactor \gent.ts\ to compute \supportsImages\ even when no session key is present, ensuring text-only override requests without sessions safely drop attachments.
- Update catalog lookups in \chat.ts\, \gent.ts\, and \server-node-events.ts\ to strictly match both \id\ and \provider\ to prevent cross-provider model collisions.
* fix(agents): restore before_install hook for skill installs
Restore the plugin scanner security hook that was accidentally dropped during merge conflict resolution.
* fix: resolve attachment pathing, defer parsing after auth gates, and clean up node-event mocks
* fix: resolve syntax errors in test-env, fix missing helper imports, and optimize parsing sequence in node events
* fix(gateway): re-enforce message length limit after attachment parsing
Adds a secondary check to ensure the 20,000-char cap remains effective even after media markers are appended during the offload flow.
* fix(gateway): prevent dropping valid small images and clean up orphaned media on size rejection
* fix(gateway): share attachment image capability checks
* fix(gateway): preserve mixed attachment order
* fix: fail closed on unknown image capability (#55513) (thanks @Syysean)
* fix: classify offloaded attachment refs explicitly (#55513) (thanks @Syysean)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix: make cleanup "keep" persist subagent sessions indefinitely
* feat: expose subagent session metadata in sessions list
* fix: include status and timing in sessions_list tool
* fix: hide injected timestamp prefixes in chat ui
* feat: push session list updates over websocket
* feat: expose child subagent sessions in subagents list
* feat: add admin http endpoint to kill sessions
* Emit session.message websocket events for transcript updates
* Estimate session costs in sessions list
* Add direct session history HTTP and SSE endpoints
* Harden dashboard session events and history APIs
* Add session lifecycle gateway methods
* Add dashboard session API improvements
* Add dashboard session model and parent linkage support
* fix: tighten dashboard session API metadata
* Fix dashboard session cost metadata
* Persist accumulated session cost
* fix: stop followup queue drain cfg crash
* Fix dashboard session create and model metadata
* fix: stop guessing session model costs
* Gateway: cache OpenRouter pricing for configured models
* Gateway: add timeout session status
* Fix subagent spawn test config loading
* Gateway: preserve operator scopes without device identity
* Emit user message transcript events and deduplicate plugin warnings
* feat: emit sessions.changed lifecycle event on subagent spawn
Adds a session-lifecycle-events module (similar to transcript-events)
that emits create events when subagents are spawned. The gateway
server.impl.ts listens for these events and broadcasts sessions.changed
with reason=create to SSE subscribers, so dashboards can pick up new
subagent sessions without polling.
* Gateway: allow persistent dashboard orchestrator sessions
* fix: preserve operator scopes for token-authenticated backend clients
Backend clients (like agent-dashboard) that authenticate with a valid gateway
token but don't present a device identity were getting their scopes stripped.
The scope-clearing logic ran before checking the device identity decision,
so even when evaluateMissingDeviceIdentity returned 'allow' (because
roleCanSkipDeviceIdentity passed for token-authed operators), scopes were
already cleared.
Fix: also check decision.kind before clearing scopes, so token-authenticated
operators keep their requested scopes.
* Gateway: allow operator-token session kills
* Fix stale active subagent status after follow-up runs
* Fix dashboard image attachments in sessions send
* Fix completed session follow-up status updates
* feat: stream session tool events to operator UIs
* Add sessions.steer gateway coverage
* Persist subagent timing in session store
* Fix subagent session transcript event keys
* Fix active subagent session status in gateway
* bump session label max to 512
* Fix gateway send session reactivation
* fix: publish terminal session lifecycle state
* feat: change default session reset to effectively never
- Change DEFAULT_RESET_MODE from "daily" to "idle"
- Change DEFAULT_IDLE_MINUTES from 60 to 0 (0 = disabled/never)
- Allow idleMinutes=0 through normalization (don't clamp to 1)
- Treat idleMinutes=0 as "no idle expiry" in evaluateSessionFreshness
- Default behavior: mode "idle" + idleMinutes 0 = sessions never auto-reset
- Update test assertion for new default mode
* fix: prep session management followups (#50101) (thanks @clay-datacurve)
---------
Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>