fix: preserve interaction replies across chat recovery
This commit is contained in:
@@ -1,5 +1,34 @@
|
||||
export type InputDelivery = "steer" | "queue";
|
||||
export type ResponseInput = { id: string; text: string; delivery: InputDelivery; status: "pending" | "delivered" | "cancelled" };
|
||||
type WaitingInboxRuntime = {
|
||||
listPendingUserInputs: (sessionId: string) => Promise<ResponseInput[]>;
|
||||
agentMessageState: (sessionId: string, id: string) => Promise<"missing" | "enqueued" | "delivered">;
|
||||
enqueueUserInput: (sessionId: string, input: ResponseInput, options?: { resume?: boolean }) => Promise<void>;
|
||||
};
|
||||
|
||||
/** A question boundary admits input without starting an unowned model loop. */
|
||||
export class WaitingResponseInputQueue {
|
||||
private readonly operations = new Map<string, Promise<ResponseInput>>();
|
||||
constructor(private readonly runtime: WaitingInboxRuntime) {}
|
||||
|
||||
async submit(sessionId: string, input: ResponseInput): Promise<ResponseInput> {
|
||||
const operation = (this.operations.get(sessionId) ?? Promise.resolve()).catch(() => undefined).then(async () => {
|
||||
const pending = await this.runtime.listPendingUserInputs(sessionId);
|
||||
const previous = pending.find((item) => item.id === input.id);
|
||||
if (previous) {
|
||||
if (previous.text !== input.text) throw Object.assign(new Error("Input ID already belongs to another message"), { status: 409 });
|
||||
return previous;
|
||||
}
|
||||
if (await this.runtime.agentMessageState(sessionId, input.id) === "delivered") return { ...input, status: "delivered" as const };
|
||||
if (pending.length >= 20) throw Object.assign(new Error("At most 20 user messages may wait in this session"), { status: 429 });
|
||||
await this.runtime.enqueueUserInput(sessionId, input, { resume: false });
|
||||
return { ...input };
|
||||
});
|
||||
this.operations.set(sessionId, operation);
|
||||
try { return await operation; }
|
||||
finally { if (this.operations.get(sessionId) === operation) this.operations.delete(sessionId); }
|
||||
}
|
||||
}
|
||||
type InboxRuntime = {
|
||||
enqueueUserInput: (sessionId: string, input: ResponseInput) => Promise<void>;
|
||||
listPendingUserInputs: (sessionId: string) => Promise<ResponseInput[]>;
|
||||
|
||||
Reference in New Issue
Block a user