diff --git a/CHANGELOG.md b/CHANGELOG.md index a8094c04..194a276d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ ### Fixed +- **Stream teardown no longer leaks per-stream runtime state on long-running servers.** When a stream ended, its `STREAM_SESSION_OWNERS` owner mapping and (for gateway turns) the `AGENT_INSTANCES` entry were retained, so a high-volume, long-lived server slowly accumulated dead per-stream bookkeeping. Teardown now unregisters the stream owner and drops the gateway agent instance. The offline-frame buffer is deliberately **not** cleared on teardown so a late reconnect can still replay buffered `token`/`done`/`stream_end` frames; buffers remain bounded (8,192 frames) and channels are reclaimed once outstanding reconnect references exit. Thanks @webtecnica. (#6389, #6351) + - **Japanese UI is fully translated again.** The Japanese (`ja`) locale had drifted to English fallback across recent features (workspace panel, session sharing, extensions gallery, cron sentence-builder, composer, settings, and update flows); it is now refreshed to parity with the current English keys — 180 strings — so Japanese users no longer see a mixed-language UI. Thanks @koshikai. (#6403) - **Reloaded tool-call turns render with the same unified Worklog as live turns.** Historical transcripts using ID-linked tool calls (OpenAI-shape `assistant.tool_calls[].id` ↔ `role:tool.tool_call_id`) previously rendered through the legacy renderer after a reload or session switch, so a reloaded turn could look different from how it appeared live. Unambiguous, complete ID-linked turns now hydrate into the unified Assistant Turn Anchor before render, matching the live presentation exactly while failing closed to the existing renderer for any richer or ambiguous case. Thanks @franksong2702. (#6222, #6220) diff --git a/api/gateway_chat.py b/api/gateway_chat.py index 006ad390..4f1fbdee 100644 --- a/api/gateway_chat.py +++ b/api/gateway_chat.py @@ -12,6 +12,7 @@ import urllib.request from typing import Any from api.config import ( + AGENT_INSTANCES, CANCEL_FLAGS, PENDING_GOAL_CONTINUATION, STREAM_GOAL_RELATED, @@ -1187,6 +1188,7 @@ def _run_gateway_chat_streaming( logger.debug("Failed to clear gateway stream state", exc_info=True) _cleanup_gateway_pending_mirror(session_id) with STREAMS_LOCK: + AGENT_INSTANCES.pop(stream_id, None) CANCEL_FLAGS.pop(stream_id, None) STREAM_GOAL_RELATED.pop(stream_id, None) STREAM_PARTIAL_TEXT.pop(stream_id, None) @@ -1195,4 +1197,5 @@ def _run_gateway_chat_streaming( STREAM_LAST_EVENT_ID.pop(stream_id, None) STREAMS.pop(stream_id, None) _STREAM_RUN_IDS.pop(stream_id, None) + unregister_stream_owner(stream_id) unregister_active_run(stream_id) diff --git a/api/streaming.py b/api/streaming.py index 03b562c7..2528baf4 100644 --- a/api/streaming.py +++ b/api/streaming.py @@ -11001,6 +11001,9 @@ def _run_agent_streaming( STREAM_GOAL_RELATED.pop(stream_id, None) # Clean up goal-related flag (#1932) STREAM_LAST_EVENT_ID.pop(stream_id, None) # Clean up event_id pointer (stage-364) unregister_active_run(stream_id) + # Clean up the stream-owner registry so stale stream_id→session_id + # mappings do not accumulate over thousands of completed streams (#6351). + unregister_stream_owner(stream_id) # NOTE: do NOT discard PENDING_GOAL_CONTINUATION here. The marker # is set by goal_continue (line ~3328) inside the SAME function # call and consumed atomically by `_start_chat_stream_for_session`