06.2: Response Sink
このコンテンツはまだ日本語訳がありません。
The response sink connects provider streaming to Discord delivery. It resolves whether output routes through an alter-persona webhook, a temporary user-impersonation webhook, or standard bot messages, and manages delivery lifecycle callbacks.
Flow and ownership
Section titled “Flow and ownership”createChatResponseSink() in src/utils/chat/responseEmitter.ts constructs a ChatResponseSink
for the current ChatTurnContext:
ChatTurnContext │ ▼createChatResponseSink() ─────────────────► returns ChatResponseSink │ ├─► prepare() ──► resolves ChatResponseTarget, arms channel lock ├─► emitStreamResult() ──► filters duplicate errors, sends generic error embeds ├─► emitError() ──► surfaces exceptions (rethrows on impersonation) ├─► finalize() ──► deletes temporary webhooks, logs turn metrics └─► cleanup() ──► webhook deletion attempt in finally blockDelivery target resolution
Section titled “Delivery target resolution”prepare() executes at the start of generation and resolves ChatResponseTarget:
- User impersonation: when
isUserImpersonationis true, it creates a temporary webhook using the target user’s display name and avatar. - Alter personas: when
currentPersona.is_alteris true, it resolves the server-owned webhook viagetOrCreateWebhook()insrc/utils/discord/webhookManager.ts. - Main persona and DMs: returns
undefined. Direct messages and standard bot replies route throughchannel.send()as the bot account.
After target resolution, prepare() updates the channel lock entry with the active persona ID,
follow-up eligibility, and impersonation status.
The response target is a transport hint. The invoking currentPersona supplies the author to
sendPersonaPost(), which can lazily resolve a webhook for a main persona’s remembered sprite.
Streaming sprite selection therefore does not need to mutate the tool loop’s response target.
See Discord delivery for the shared author,
appearance, and receipt contract.
StreamingContext.deliveryMode explicitly identifies persona or user_impersonation delivery.
Context construction and generation entry set it from the turn’s impersonation flag, including
manual turns. Every provider copies it through buildStreamContext(). A username, avatar, or
webhook on a main persona does not imply user impersonation. The same streaming context carries
the receipt array and terminal delivery state across provider attempts. An unconfirmed Discord POST
stops the turn before another provider attempt or recovery notice; held drafts are discarded.
Error emission and suppression
Section titled “Error emission and suppression”emitStreamResult(): handles completed streams ending in error status. Ifresult.datais an identifiedProviderError, the sink neither logs nor sends an embed: the orchestrator already reported the failure, andStreamErrorUi.handleProviderError()already rendered the provider-specific notice. Unhandled non-provider errors emit a generic generation error embed whencontext.shouldSurfaceUserErrorsis true.emitError(): handles unexpected exceptions. Under user impersonation, it rethrows the exception so the initiating slash command handles the failure directly, preventing error embeds from appearing as messages from the impersonated user.
Webhook cleanup lifecycle
Section titled “Webhook cleanup lifecycle”Temporary impersonation webhooks must be deleted when generation finishes:
finalize()deletes the temporary webhook after successful or non-fatal generation turns.cleanup()runs from afinallyblock inrunGenerationTurn(), attempting deletion if an exception bypassesfinalize().- Deletion is guarded by an internal boolean flag so
finalize()andcleanup()never attempt duplicate deletion calls. Failures during deletion are logged as warnings and do not throw.
Constraints and rationale
Section titled “Constraints and rationale”- Cleanup ownership: normal finalization and exception cleanup share one deletion attempt. Discord failures are logged; a failed deletion can leave a temporary webhook in the guild.
- Error deduplication: suppressing generic embeds for structured
ProviderErrorpayloads avoids displaying duplicate error notices for content filters and quota exhaustion. - Impersonation isolation: rethrowing exceptions during impersonation prevents error embeds from rendering with the impersonated user’s identity.
Source pointers
Section titled “Source pointers”src/utils/chat/responseEmitter.ts:createChatResponseSink()and target resolution.src/utils/discord/webhookManager.ts: alter persona webhook creation and reuse.src/utils/chat/types.ts:ChatResponseSinkandChatResponseTargetinterface definitions.