const moment = require("moment");
const {
applyBranding,
} = require("../agents/aibitat/plugins/create-files/pdf/utils.js");
const { convertToChatHistory } = require("../helpers/chat/responses.js");
const THOUGHT_TAGS = "thinking|think|thought|thought_chain";
const validExportTypes = ["pdf", "markdown", "plaintext", "json", "html"];
// Extract thought chain content from assistant messages.
function extractThoughtChain(text = "") {
const matches = [];
const regex = new RegExp(
`<(${THOUGHT_TAGS})[^>]*>([\\s\\S]*?)(${THOUGHT_TAGS})\\s*>`,
"gi"
);
let m;
while ((m = regex.exec(text)) !== null) matches.push(m[2].trim());
return matches.join("\n\n") || null;
}
// Strip the assistant's thought chain so the export only contains the response.
function stripThoughtChain(text = "") {
return text
.replace(
new RegExp(
`<(${THOUGHT_TAGS})[^>]*>[\\s\\S]*?(${THOUGHT_TAGS})\\s*>`,
"gi"
),
""
)
.replace(new RegExp(`?(${THOUGHT_TAGS}|response|answer)[^>]*>`, "gi"), "")
.trim();
}
// Render uploaded image attachments as markdown images so they appear in the PDF.
function imagesToMarkdown(attachments = []) {
return attachments
.filter((a) => a?.contentString?.startsWith("data:image"))
.map((a) => ``)
.join("\n\n");
}
// Build a clean markdown document from a converted chat history.
function chatHistoryToMarkdown(history = [], { workspaceName, threadName }) {
const lines = [`**Workspace:** ${workspaceName} `];
if (threadName) lines.push(`**Thread:** ${threadName} `);
lines.push(`**Exported:** ${moment().format("MMMM D, YYYY h:mm A")}`, "");
for (const msg of history) {
if (msg.role !== "user" && msg.role !== "assistant") continue;
const content =
msg.role === "assistant"
? stripThoughtChain(msg.content)
: (msg.content || "").trim();
const images = imagesToMarkdown(msg.attachments);
if (!content && !images) continue;
lines.push(
"---",
"",
`**${msg.role === "user" ? "You" : "Assistant"}**`,
""
);
if (content) lines.push(content, "");
if (images) lines.push(images, "");
}
return lines.join("\n");
}
function chatHistoryToPlainText(history = [], { workspaceName, threadName }) {
const lines = [`Workspace: ${workspaceName}`];
if (threadName) lines.push(`Thread: ${threadName}`);
lines.push(`Exported: ${moment().format("MMMM D, YYYY h:mm A")}`, "");
for (const msg of history) {
if (msg.role !== "user" && msg.role !== "assistant") continue;
const content =
msg.role === "assistant"
? stripThoughtChain(msg.content)
: (msg.content || "").trim();
if (!content) continue;
const label = msg.role === "user" ? "You" : "Assistant";
lines.push("─".repeat(60), "", `[${label}]`, "", content, "");
}
return lines.join("\n");
}
function chatHistoryToJSON(history = [], { workspaceName, threadName }) {
const messages = [];
for (const msg of history) {
if (msg.role !== "user" && msg.role !== "assistant") continue;
const content =
msg.role === "assistant"
? stripThoughtChain(msg.content)
: (msg.content || "").trim();
if (!content) continue;
const entry = { role: msg.role, content };
if (msg.role === "assistant") {
const reasoning = extractThoughtChain(msg.content || "");
if (reasoning) entry.reasoning = reasoning;
}
messages.push(entry);
}
return JSON.stringify(
{
workspace: workspaceName,
thread: threadName || null,
exported: moment().toISOString(),
messages,
},
null,
2
);
}
function chatHistoryToHTML(history = [], { workspaceName, threadName }) {
const rawTitle = threadName
? `${workspaceName} — ${threadName}`
: workspaceName;
const exported = moment().format("MMMM D, YYYY h:mm A");
function escapeHtml(str) {
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/\n/g, "
");
}
const title = escapeHtml(rawTitle);
const msgs = [];
for (const msg of history) {
if (msg.role !== "user" && msg.role !== "assistant") continue;
const rawContent = (msg.content || "").trim();
const content =
msg.role === "assistant" ? stripThoughtChain(rawContent) : rawContent;
const reasoning =
msg.role === "assistant" ? extractThoughtChain(rawContent) : null;
const images = (msg.attachments || [])
.filter((a) => a?.contentString?.startsWith("data:image"))
.map(
(a) =>
``
)
.join("\n");
if (!content && !images && !reasoning) continue;
const escapedContent = content ? escapeHtml(content) : "";
const reasoningBlock = reasoning
? `
Show reasoning
Exported ${exported}