Wire the 409 merge-retry transport into the chat (the one-line seam)

chat.tsx now uses createChatTransport(chatId): a send racing the wake
pipeline waits for the in-flight turn, merges onto the fresh server
record, and re-POSTs instead of surfacing the 409 banner. Also fixes the
transport's import to the core barrel (shell import law).

The optional "server-ahead heal" line in the jobs poller was considered
and skipped: the racing turn IS the wake turn, whose completion flips the
job to done within a poll tick or two — the existing settle-triggered
syncFromServer already heals the post-merge gap within ~1-2s. The extra
record fetch on every poll tick would double poll traffic for a path the
settle sync already covers.
This commit is contained in:
amosblomqvist
2026-09-05 21:36:04 +03:00
parent b30192ff77
commit c014d22c9f
2 changed files with 4 additions and 3 deletions
+1 -1
View File
@@ -21,7 +21,7 @@
* a future "server-ahead" heal in the poller). Rare path; self-heals. * a future "server-ahead" heal in the poller). Rare path; self-heals.
*/ */
import { DefaultChatTransport } from "ai"; import { DefaultChatTransport } from "ai";
import type { TeacherUIMessage } from "@/src/core/ui-message"; import type { TeacherUIMessage } from "@/src/core";
/** How often to poll turnInFlight while the racing turn runs. */ /** How often to poll turnInFlight while the racing turn runs. */
const TURN_POLL_MS = 500; const TURN_POLL_MS = 500;
+3 -2
View File
@@ -2,7 +2,8 @@
import { createContext, Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { createContext, Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { useChat } from "@ai-sdk/react"; import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport, isToolUIPart, lastAssistantMessageIsCompleteWithToolCalls } from "ai"; import { isToolUIPart, lastAssistantMessageIsCompleteWithToolCalls } from "ai";
import { createChatTransport } from "@/components/chat-transport";
import { useStickToBottomContext } from "use-stick-to-bottom"; import { useStickToBottomContext } from "use-stick-to-bottom";
import { import {
Conversation, Conversation,
@@ -81,7 +82,7 @@ export function Chat({
useChat<TeacherUIMessage>({ useChat<TeacherUIMessage>({
id: chatId, id: chatId,
messages: initialMessages, messages: initialMessages,
transport: new DefaultChatTransport({ api: "/api/chat", body: { chatId } }), transport: createChatTransport(chatId),
// After the learner answers (addToolOutput), automatically send the // After the learner answers (addToolOutput), automatically send the
// continuation request so the teacher reacts to the answer. // continuation request so the teacher reacts to the answer.
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls, sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,