75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
/**
|
|
* Project thread messages into text-only history for the agent edit API.
|
|
* Includes edit_elements apply outcomes so a refused client apply is visible
|
|
* to the model on the next turn (tool-call parts are otherwise dropped).
|
|
*/
|
|
import {
|
|
editElementsOutcome,
|
|
editElementsRefuseReason,
|
|
} from '@/lib/agent/client/edit-elements-result';
|
|
|
|
export type HistoryTurn = { role: 'user' | 'assistant'; text: string };
|
|
|
|
type ContentPart = {
|
|
type?: string;
|
|
text?: string;
|
|
toolName?: string;
|
|
result?: {
|
|
content?: { type?: string; text?: string }[];
|
|
details?: {
|
|
intents?: unknown[] | null;
|
|
updateCount?: number;
|
|
refuseReason?: string;
|
|
};
|
|
};
|
|
isError?: boolean;
|
|
};
|
|
|
|
function editElementsOutcomeLine(part: ContentPart): string | null {
|
|
if (part.type !== 'tool-call' || part.toolName !== 'edit_elements') return null;
|
|
const details = part.result?.details;
|
|
const outcome = editElementsOutcome(details);
|
|
if (outcome === 'refused') {
|
|
const reason = editElementsRefuseReason(part.result) ?? 'refused';
|
|
return `[edit_elements: not applied — ${reason}]`;
|
|
}
|
|
if (outcome === 'applied' && !part.isError) {
|
|
const intents = details?.intents;
|
|
if (!Array.isArray(intents)) return null;
|
|
const n = typeof details?.updateCount === 'number' ? details.updateCount : intents.length;
|
|
return `[edit_elements: applied ${n} update(s)]`;
|
|
}
|
|
if (part.isError) {
|
|
return `[edit_elements: not applied]`;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** Flatten one message's content into history text (incl. edit_elements outcomes). */
|
|
export function messageTextForHistory(content: unknown): string {
|
|
if (typeof content === 'string') return content.trim();
|
|
if (!Array.isArray(content)) return '';
|
|
const chunks: string[] = [];
|
|
for (const raw of content) {
|
|
const p = raw as ContentPart;
|
|
if (p && p.type === 'text' && typeof p.text === 'string' && p.text.trim()) {
|
|
chunks.push(p.text.trim());
|
|
}
|
|
const outcome = editElementsOutcomeLine(p);
|
|
if (outcome) chunks.push(outcome);
|
|
}
|
|
return chunks.join('\n').trim();
|
|
}
|
|
|
|
export function toAgentHistory(
|
|
messages: Array<{ role?: string; content?: unknown }>,
|
|
): HistoryTurn[] {
|
|
const out: HistoryTurn[] = [];
|
|
for (const m of messages) {
|
|
const text = messageTextForHistory(m.content);
|
|
if (!text) continue;
|
|
out.push({ role: m.role === 'user' ? 'user' : 'assistant', text });
|
|
}
|
|
return out;
|
|
}
|