v2026.04.30
v2026.04.30
Highlights
Universal Widget Context
The agent now sees what you're seeing on the dashboard. Every widget grows a "+" button, and list-shaped widgets (news feed, watchlist, insight briefs, earnings calendar) get a per-row attach button — click either to drop the widget (or a single row) into your next chat message. Charts attach as JPEG snapshots for vision-capable models, with text-only directive fallback when the active model can't see images; structured widgets emit a <widget-context> directive carrying the shape the agent needs (OHLCV bars, news headlines, watchlist quotes, brief summaries) without burning a vision turn. A floating deck rail above the chat input shows everything you've attached — top card visible, peek-stack behind, fan-out on hover — and stays in sync across multiple chat inputs if you've got more than one open.
Chat Tool-Call UX Refresh
Tool-call cards in the conversation timeline are now path-aware. When the agent touches .agents/skills/..., memory/..., or memo/..., the card reads Activated skill , Read memory about , or Read memo instead of flattening into a generic "Read filename". Click any of those pills and the right panel snaps to the matching tab — Memory, Memo, or Files — with the entry pre-selected. The activity accordion is now a real monochrome timeline with a vertical rule, neutral gray ✓/✕ badges for completed and failed steps, and aria-live announcements so screen readers know when a tool call finishes.
Performance & Robustness
A wide pass of plumbing fixes that show up as faster turns, cheaper BYOK runs, and threads that no longer get stuck or break.
Performance
- Subagent fan-out memory leaks fixed. Worker heap was stair-stepping under sustained subagent fan-out — completed background tasks were retaining their compiled LangGraph, every middleware closure, the completion callback, and an unbounded list of captured events (a single fan-out could pin 100k+ event objects) for the full 24h result TTL, never reclaimed. Terminal tasks now release those heavy refs immediately, the per-task event list is bounded with Redis spillover for SSE replay, and a shared LangSmith tracer race that was leaking tracker state into parent middleware lifetime is fixed. Workers stay flat under load instead of climbing toward OOM.
- Idle MCP subprocesses dropped to zero. Each active workspace previously spawned its own MCP cohort —
N_workspaces × 10long-lived stdio subprocesses sitting idle and contributing to worker memory pressure. The registry is now frozen once at backend boot and shared across every workspace, so workspace startup is faster and idle resource use no longer scales with concurrent workspace count. - OpenAI and Codex cache pinning. Multi-turn conversations on OpenAI- and Codex-class models are now pinned to a thread-stable cache key so every turn lands on the same upstream cache shard. Faster responses and meaningfully lower token spend on long threads, especially for BYOK users.
Robustness
- Anthropic-compatible BYOK endpoints. Stray signature-only thinking blocks are now sanitized before they leave the middleware chain, so adaptive-thinking turns against DeepSeek's
/anthropicendpoint, Z.AI, and MiniMax stop failing withmessages[i].content: missing field 'thinking'. - Threads no longer brick on NUL bytes. A tool result containing a NUL byte (common in sandbox stdout, web-fetch markdown, or PDF font glyphs) used to poison the persistence path — and because the bad message also lived in the LangGraph checkpoint, every resume of that thread would replay it and fail again. NUL is now stripped at the source and at every Postgres bind boundary, and a recovery script (
scripts/ops/scrub_nul_checkpoint.py) ships for already-poisoned threads. - Memo uploads are tougher and faster. S3 uploads now run outside the per-namespace lock, so concurrent memo ops on the same account stop blocking each other for the 3–5 seconds the round trip takes. Deleting a memo on one worker now cancels its in-flight metadata generation on a different worker via a Redis cooperative cancel signal. PDFs that previously 503'd on
pdfminerNUL-byte glyphs upload cleanly, and burst regenerates within the same wall-clock second no longer collide. - Cleaner failure feedback. When a workflow dies (cancelled, max-retries-exceeded, soft-interrupted, or hit a setup error), it now writes a real terminal status to Redis with bounded TTL instead of leaving it stuck on
ACTIVE. The frontend stops trying to reconnect to streams that will never emit events again. - Model picker. Settings → Model and the Setup wizard's defaults step now show your full accessible model list — they were incorrectly scoped to your starred subset. Custom models that share a name with a built-in no longer render a duplicate row.
Changelog
New Features
- dashboard: universal widget context for the agent (#180) (19a41e1)
- chat-agent: path-aware tool labels + memory/memo panel routing + monochrome timeline (#181) (70faa9a)
- dashboard: widget i18n complete + locale-aware formatters (#179) (3b973eb)
- llms: pin OpenAI/Codex requests to thread-stable prompt_cache_key (#191) (61e1b40)
- workflow: canonical terminal status wiring + invariant-pinned reconnect set (#188) (2680d97)
- mcp: freeze global registry at startup, drop per-workspace cohorts (#186) (cadc0be)
- subagent: bound per-task heap, durable Redis-spilled SSE history (#185) (ab5d2d2)
Bug Fixes
- persistence: strip NUL bytes from tool results and Postgres TEXT/JSONB binds (#190) (1e46d6d)
- agent: restore Anthropic thinking sanitizer wiring (regression from #150) (#182) (8d01c13)
- memo: hardening + cross-worker metadata cancel (#178) (fb4a036)
- model-picker: show full accessible list + hide shadowed built-ins (#177) (cec73c3)
7 features, 4 fixes — 11 commits total