Skip to content

Commit 893069b

Browse files
authored
fix(decopilot): publish user-prompt mirror after run purge so mid-run joiners see it (#4464)
Move the data-user-message publish from POST time (routes.ts) into dispatchRun, right after streamBuffer.purge() clears the previous run. Published before, the chunk was wiped by that purge, so a viewer JOINING mid-run (deliverPolicy: "all") replayed only the assistant reply, not the prompt, until a DB refetch. Publishing after the purge lets it survive the whole run. Also aligns queued-turn UX (prompt appears at dispatch time).
1 parent 8918ee8 commit 893069b

2 files changed

Lines changed: 15 additions & 5 deletions

File tree

apps/mesh/src/api/routes/decopilot/dispatch-run.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ import {
9797
publishRunStatusStage,
9898
shouldPublishClusterRunStatus,
9999
} from "./run-status-stage";
100+
import { publishUserMessage } from "./user-message-stream";
100101
import type {
101102
HarnessStreamConsumerHooks,
102103
HarnessStreamTitleOptions,
@@ -1277,6 +1278,20 @@ async function prepareRun(
12771278
}
12781279
}
12791280

1281+
// Mirror the user prompt onto the run stream AFTER the purge above (which
1282+
// clears the previous run) so it survives the whole run — a viewer who
1283+
// JOINS mid-run replays it via `deliverPolicy: "all"`, not only viewers
1284+
// already connected at POST time. Best-effort and published before the
1285+
// run's assistant chunks so it sorts first. Uses the persisted message
1286+
// shape so the live chunk and a later DB refetch reconcile by id, no swap.
1287+
if (materializedRequestMessage) {
1288+
await publishUserMessage(
1289+
streamBuffer,
1290+
mem.thread.id,
1291+
materializedRequestMessage as UIMessage,
1292+
);
1293+
}
1294+
12801295
const pendingOps: Promise<void>[] = [];
12811296

12821297
// CLI-harness delta + resume only holds on the long-lived desktop daemon:

apps/mesh/src/api/routes/decopilot/routes.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import {
4545
publishRunStatusStage,
4646
shouldPublishClusterRunStatus,
4747
} from "./run-status-stage";
48-
import { publishUserMessage } from "./user-message-stream";
4948
import { wrapWithSseKeepalive } from "./sse-keepalive";
5049
import { resolveDispatchTarget } from "../../../links/resolve-dispatch-target";
5150
import {
@@ -747,10 +746,6 @@ export function createDecopilotRoutes(deps: DecopilotDeps) {
747746
})
748747
) {
749748
await emitter.emitRequestMessage(persistedRequestMessage);
750-
// Mirror the prompt onto the run stream so OTHER viewers of a shared
751-
// thread render it live, not only after a DB refetch. Best-effort and
752-
// published before the run's assistant chunks so it sorts first.
753-
await publishUserMessage(streamBuffer, taskId, persistedRequestMessage);
754749
}
755750

756751
const serializableRequest = buildDurableDispatchInput(input, {

0 commit comments

Comments
 (0)