fix: persist SDK session_id on init + split long messages before adapter truncation

Two related bugs that surfaced together when a Discord response exceeded
2000 chars:

1. **Session id lost on mid-turn container exit.** `runPollLoop` was
   calling `setStoredSessionId` only after `processQuery` returned. If
   the container died between the SDK's `init` event (where session_id
   arrives) and the stream completing, the id was never persisted. The
   next wake called `getStoredSessionId()` → undefined and started a
   fresh Claude session, dropping all prior context. Fix: persist
   immediately in the `init` branch inside `processQuery`. The existing
   post-query store becomes a harmless no-op.

2. **Silent truncation past adapter limits.** `chat-sdk-bridge.deliver`
   handed full text straight to `adapter.postMessage`. Discord's adapter
   hard-truncates at 2000 chars; Telegram's at 4096. Responses longer
   than that were cut off without any signal to the user or host. Fix:
   add `maxTextLength` to `ChatSdkBridgeConfig` and a `splitForLimit`
   helper that breaks on paragraph → line → hard-char boundaries, then
   posts chunks sequentially. Files ride on the first chunk; the
   returned id is the first chunk's so edits and reactions still target
   the reply head.

Channel adapter files (Discord, Telegram, …) live on the `channels`
branch — a companion PR wires `maxTextLength: 1900` for Discord and
`4000` for Telegram so the splitter actually engages in those installs.
Without wiring, behavior is unchanged.
This commit is contained in:
Dave Kim
2026-04-21 13:04:57 +00:00
parent c9977d6b69
commit 91c668e0cc
3 changed files with 84 additions and 7 deletions

View File

@@ -322,6 +322,13 @@ async function processQuery(query: AgentQuery, routing: RoutingContext): Promise
if (event.type === 'init') {
queryContinuation = event.continuation;
// Persist immediately so a mid-turn container crash still lets the
// next wake resume the conversation. Without this, the session id
// was only written after the full stream completed — if the
// container died between `init` and `result`, the SDK session was
// effectively orphaned and the next message started a blank
// Claude session with no prior context.
setStoredSessionId(event.continuation);
} else if (event.type === 'result' && event.text) {
dispatchResultText(event.text, routing);
}