refactor: consolidate chat state and task bridges
Centralize session scope, chat projections and typed task bridges. Keep historical interaction replies authoritative and publish terminal state to subscribers. Validate Server contracts before replacing generated artifacts.
This commit is contained in:
+45
-36
@@ -15,6 +15,8 @@ export type StreamSubscriber = {
|
||||
};
|
||||
|
||||
export type ActiveRun = {
|
||||
applyEvent?: (event: string, data: Record<string, unknown>) => void;
|
||||
uiMessages?: () => unknown[];
|
||||
inputs?: ResponseInputTracker;
|
||||
clientSessionId: string;
|
||||
controller: AbortController;
|
||||
@@ -175,43 +177,50 @@ export const updateLastAssistantMessage = (
|
||||
return messages;
|
||||
};
|
||||
|
||||
export const updateLastAssistantPermission = (
|
||||
messages: unknown[],
|
||||
requestId: string,
|
||||
updater: (permission: Record<string, unknown>) => Record<string, unknown>,
|
||||
) =>
|
||||
updateLastAssistantMessage(messages, (message) => {
|
||||
const permissions = Array.isArray(message.permissions)
|
||||
? message.permissions
|
||||
: [];
|
||||
return {
|
||||
...message,
|
||||
permissions: permissions.map((permission) =>
|
||||
isObjectRecord(permission) && permission.requestId === requestId
|
||||
? updater(permission)
|
||||
: permission,
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
export const updateLastAssistantQuestion = (
|
||||
messages: unknown[],
|
||||
requestId: string,
|
||||
updater: (question: Record<string, unknown>) => Record<string, unknown>,
|
||||
) =>
|
||||
updateLastAssistantMessage(messages, (message) => {
|
||||
const questions = Array.isArray(message.questions)
|
||||
? message.questions
|
||||
: [];
|
||||
return {
|
||||
...message,
|
||||
questions: questions.map((question) =>
|
||||
isObjectRecord(question) && question.requestId === requestId
|
||||
? updater(question)
|
||||
: question,
|
||||
),
|
||||
};
|
||||
/** Replies belong to the assistant that requested input, including earlier turns. */
|
||||
export function applyInteractionResponse<T>(
|
||||
messages: T[],
|
||||
event: "permission_response" | "question_response",
|
||||
data: Record<string, unknown>,
|
||||
): T[] {
|
||||
const requestId = data.request_id;
|
||||
if (typeof requestId !== "string") return messages;
|
||||
const permission = event === "permission_response";
|
||||
const field = permission ? "permissions" : "questions";
|
||||
const requestType = permission ? "data-permission_request" : "data-question_request";
|
||||
let changed = false;
|
||||
const next = messages.map((message) => {
|
||||
if (!isObjectRecord(message) || message.role !== "assistant") return message;
|
||||
const entries = Array.isArray(message[field]) ? message[field] as unknown[] : undefined;
|
||||
const parts = Array.isArray(message.parts) ? [...message.parts] : undefined;
|
||||
const ownsRequest = entries?.some((entry) => isObjectRecord(entry) && entry.requestId === requestId)
|
||||
|| parts?.some((part) => isObjectRecord(part) && part.type === requestType
|
||||
&& isObjectRecord(part.data) && part.data.request_id === requestId);
|
||||
if (!ownsRequest) return message;
|
||||
changed = true;
|
||||
const updated = entries?.map((entry) => {
|
||||
if (!isObjectRecord(entry) || entry.requestId !== requestId) return entry;
|
||||
return permission ? {
|
||||
...entry, status: data.status === "error" ? "error" : toPermissionStatus(data.reply as PermissionReply),
|
||||
error: data.status === "error" ? data.error : undefined, repliedAt: Date.now(),
|
||||
} : {
|
||||
...entry, status: typeof data.status === "string" ? data.status : data.rejected === true ? "rejected" : "answered",
|
||||
...(Array.isArray(data.answers) ? { answers: data.answers } : {}), error: undefined, repliedAt: Date.now(),
|
||||
};
|
||||
});
|
||||
if (parts && data.status !== "pending") {
|
||||
const type = `data-${event}`;
|
||||
const index = parts.findIndex((part) => isObjectRecord(part) && part.type === type
|
||||
&& isObjectRecord(part.data) && part.data.request_id === requestId);
|
||||
const previous = index >= 0 && isObjectRecord(parts[index]) ? parts[index] : undefined;
|
||||
const part = { ...previous, type, data: { ...(isObjectRecord(previous?.data) ? previous.data : {}), ...data } };
|
||||
if (index >= 0) parts[index] = part;
|
||||
else parts.push(part);
|
||||
}
|
||||
return { ...message, ...(updated ? { [field]: updated } : {}), ...(parts ? { parts } : {}) };
|
||||
});
|
||||
return changed ? next : messages;
|
||||
}
|
||||
|
||||
const getToolArtifactKind = (tool: string): ToolArtifactKind => {
|
||||
if (tool === "present_view") {
|
||||
|
||||
Reference in New Issue
Block a user