diff --git a/book.json b/book.json
new file mode 100644
index 0000000..960f883
--- /dev/null
+++ b/book.json
@@ -0,0 +1,13 @@
+{
+ "plugins": [
+ "scripts"
+ ],
+ "pluginsConfig": {
+ "scripts": {
+ "files": [
+ "./scripts/agent_executor.js"
+ ]
+ }
+ }
+
+}
diff --git a/scripts/agent_executor.js b/scripts/agent_executor.js
new file mode 100644
index 0000000..20f22a9
--- /dev/null
+++ b/scripts/agent_executor.js
@@ -0,0 +1,736 @@
+(function () {
+ document.addEventListener("DOMContentLoaded", function () {
+ // -------- STATE --------
+ let expanded = false;
+ let firstAssistantShown = false;
+
+ let conversationHistory = [];
+ let collectedWorkflows = [];
+
+ async function sendToBackend(userText) {
+ try {
+ const response = await fetch("https://api.thousandmonkeystypewriter.org/chat", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ query: userText,
+ history: conversationHistory,
+ workflows: collectedWorkflows
+ }),
+ });
+
+ if (!response.ok)
+ throw new Error("Bad backend response: " + response.status);
+
+ return await response.json();
+
+ } catch (err) {
+ console.error("Backend error:", err);
+ return {
+ mode: "error",
+ message: "❌ Error contacting backend: " + err.message,
+ };
+ }
+ }
+
+ // -------- COLLAPSED CHAT STRING --------
+ const chatString = document.createElement("div");
+ chatString.id = "chat-string";
+ Object.assign(chatString.style, {
+ position: "fixed",
+ top: "110px",
+ right: "40px",
+ width: "230px",
+ borderRadius: "9999px",
+ background: "#ffffff",
+ border: "1px solid #d1d5db",
+ boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
+ display: "flex",
+ alignItems: "center",
+ gap: "8px",
+ padding: "10px 16px",
+ cursor: "text",
+ zIndex: "2147483647",
+ transition: "all 0.3s ease",
+ });
+
+ const input = document.createElement("input");
+ input.placeholder = "What do you want to run?";
+ Object.assign(input.style, {
+ flex: "1",
+ border: "none",
+ outline: "none",
+ fontSize: "14px",
+ color: "#374151",
+ background: "transparent",
+ });
+
+ const icon = document.createElement("span");
+ icon.textContent = "➤";
+ Object.assign(icon.style, {
+ color: "#2f855a",
+ fontWeight: "bold",
+ cursor: "pointer",
+ userSelect: "none",
+ });
+
+ chatString.appendChild(input);
+ chatString.appendChild(icon);
+ document.body.appendChild(chatString);
+
+ // -------- EXPANDED SIDEBAR --------
+ const sidebar = document.createElement("div");
+ Object.assign(sidebar.style, {
+ position: "fixed",
+ top: "80px",
+ right: "0",
+ width: "440px",
+ height: "calc(100vh - 80px)",
+ background: "#fff",
+ borderLeft: "1px solid #ddd",
+ boxShadow: "-4px 0 12px rgba(0,0,0,0.1)",
+ display: "flex",
+ flexDirection: "column",
+ transform: "translateX(100%)",
+ transition: "transform 0.3s ease",
+ zIndex: "2147483646",
+ fontFamily: "Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
+ });
+
+ // Header
+ const header = document.createElement("div");
+ Object.assign(header.style, {
+ padding: "12px 16px",
+ borderBottom: "1px solid #e5e7eb",
+ background: "#f9fafb",
+ fontWeight: "600",
+ color: "#111827",
+ display: "flex",
+ justifyContent: "space-between",
+ alignItems: "center",
+ fontSize: "14px",
+ });
+ header.textContent = "Docs Assistant";
+
+ const closeBtn = document.createElement("span");
+ closeBtn.textContent = "✕";
+ closeBtn.style.cursor = "pointer";
+ closeBtn.onclick = () => collapseSidebar();
+ header.appendChild(closeBtn);
+ sidebar.appendChild(header);
+
+ // Chat area
+ const chatArea = document.createElement("div");
+ Object.assign(chatArea.style, {
+ flex: "1",
+ overflowY: "auto",
+ padding: "16px",
+ fontSize: "13px",
+ color: "#2d3748",
+ display: "flex",
+ flexDirection: "column",
+ gap: "6px",
+ });
+ const placeholder = document.createElement("div");
+ placeholder.textContent = "Describe what you want to ask or execute...";
+ Object.assign(placeholder.style, {
+ textAlign: "center",
+ color: "#a0aec0",
+ marginTop: "40%",
+ });
+ chatArea.appendChild(placeholder);
+ sidebar.appendChild(chatArea);
+
+ // ---- Collapsible Suggestions ----
+ const suggestionsWrap = document.createElement("div");
+ Object.assign(suggestionsWrap.style, {
+ borderTop: "1px solid #e2e8f0",
+ background: "#f7faf7",
+ padding: "10px 16px",
+ });
+
+ // Toggle section container
+ let suggestionsOpen = true;
+
+ // Header row with text + toggle arrow
+ const suggestionsHeader = document.createElement("div");
+ Object.assign(suggestionsHeader.style, {
+ display: "flex",
+ justifyContent: "space-between",
+ alignItems: "center",
+ cursor: "pointer",
+ userSelect: "none",
+ });
+
+ const suggestionsLabel = document.createElement("span");
+ suggestionsLabel.textContent = "Suggestions";
+ Object.assign(suggestionsLabel.style, {
+ fontSize: "13px",
+ fontWeight: "600",
+ color: "#2d3748",
+ });
+
+ // Arrow icon
+ const arrow = document.createElement("span");
+ arrow.textContent = "▼";
+ Object.assign(arrow.style, {
+ fontSize: "12px",
+ marginLeft: "6px",
+ color: "#2d3748",
+ transition: "transform 0.2s ease",
+ });
+
+ suggestionsHeader.appendChild(suggestionsLabel);
+ suggestionsHeader.appendChild(arrow);
+ suggestionsWrap.appendChild(suggestionsHeader);
+
+ // Container holding suggestion buttons
+ const suggestionsBody = document.createElement("div");
+ Object.assign(suggestionsBody.style, {
+ marginTop: "8px",
+ display: "block",
+ });
+
+ // Toggle behavior
+ suggestionsHeader.onclick = () => {
+ suggestionsOpen = !suggestionsOpen;
+ suggestionsBody.style.display = suggestionsOpen ? "block" : "none";
+ arrow.style.transform = suggestionsOpen ? "rotate(0deg)" : "rotate(-90deg)";
+ };
+
+
+ const makeSugBtn = (text) => {
+ const btn = document.createElement("button");
+ btn.type = "button";
+ btn.textContent = text;
+ Object.assign(btn.style, {
+ display: "block",
+ width: "100%",
+ textAlign: "left",
+ color: "#2f855a",
+ background: "none",
+ border: "none",
+ fontSize: "13px",
+ cursor: "pointer",
+ padding: "4px 0",
+ });
+ btn.onmouseover = () => (btn.style.textDecoration = "underline");
+ btn.onmouseout = () => (btn.style.textDecoration = "none");
+ btn.onclick = () => {
+ footerInput.value = text;
+ footerInput.focus();
+
+ // collapse suggestions after click
+ suggestionsOpen = false;
+ suggestionsBody.style.display = "none";
+ arrow.style.transform = "rotate(-90deg)";
+ };
+ return btn;
+ };
+
+ [
+ "How do I create and deploy a CosmWasm contract on Juno?",
+ "Query a wallet’s bank balances via the REST API",
+ "What RPC and REST endpoints should I use for Juno mainnet and testnet?",
+ "What is Juno and what makes it different from other Cosmos chains?",
+ "Broadcast a pre-signed transaction over gRPC",
+ "Set mempool max-txs to -1 in app.toml",
+ "Allow p2p port 26656 through ufw",
+ ].forEach((s) => suggestionsBody.appendChild(makeSugBtn(s)));
+
+ suggestionsWrap.appendChild(suggestionsBody);
+
+ // Footer input
+ const footer = document.createElement("form");
+ Object.assign(footer.style, {
+ borderTop: "1px solid #e2e2e2",
+ padding: "10px",
+ display: "flex",
+ gap: "6px",
+ alignItems: "center",
+ background: "#fff",
+ });
+
+ const footerInput = document.createElement("input");
+ footerInput.placeholder =
+ "What do you want to ask about Juno?";
+ Object.assign(footerInput.style, {
+ flex: "1",
+ border: "1px solid #cbd5e0",
+ borderRadius: "8px",
+ padding: "10px 12px",
+ fontSize: "13px",
+ outline: "none",
+ });
+
+ const execBtn = document.createElement("button");
+ execBtn.type = "submit";
+ execBtn.textContent = "Send";
+ Object.assign(execBtn.style, {
+ background: "#2f855a",
+ color: "white",
+ border: "none",
+ borderRadius: "8px",
+ padding: "10px 14px",
+ cursor: "pointer",
+ fontSize: "13px",
+ fontWeight: "600",
+ });
+
+ footer.appendChild(footerInput);
+ footer.appendChild(execBtn);
+
+ // Compose sidebar (order matters)
+ sidebar.appendChild(suggestionsWrap);
+ sidebar.appendChild(footer);
+ document.body.appendChild(sidebar);
+
+
+ // ========= expand/collapse =========
+ const expandSidebar = () => {
+ expanded = true;
+ sidebar.style.transform = "translateX(0)";
+ chatString.style.opacity = "0";
+ setTimeout(() => (chatString.style.display = "none"), 300);
+ // focus footer input for immediate typing
+ setTimeout(() => footerInput.focus(), 120);
+ };
+
+ const collapseSidebar = () => {
+ expanded = false;
+ sidebar.style.transform = "translateX(100%)";
+ chatString.style.display = "flex";
+ setTimeout(() => (chatString.style.opacity = "1"), 100);
+ };
+
+ function hideSuggestions() {
+ if (suggestionsWrap) {
+ suggestionsWrap.style.display = "none";
+ }
+ }
+
+ input.addEventListener("focus", expandSidebar);
+ icon.addEventListener("click", expandSidebar);
+ chatString.addEventListener("click", () => input.focus());
+
+
+ // ========= message utility ===========
+ const addMsg = (role, htmlText) => {
+ const msg = document.createElement("div");
+ msg.innerHTML = htmlText;
+
+ Object.assign(msg.style, {
+ marginBottom: "6px",
+ maxWidth: "90%",
+ padding: "8px 10px",
+ borderRadius: "10px",
+ fontSize: "13px",
+ whiteSpace: "normal",
+ wordBreak: "break-word",
+ });
+
+ if (role === "user") {
+ msg.style.background = "#dcfce7";
+ msg.style.marginLeft = "auto";
+ } else {
+ msg.style.background = "#edf2f7";
+ }
+
+ chatArea.appendChild(msg);
+ chatArea.scrollTop = chatArea.scrollHeight;
+
+ // 🔥 NEW: store message in history
+ conversationHistory.push({
+ role,
+ content: htmlText
+ });
+ };
+
+ function showExecutionModal(result) {
+ // Remove old modal if exists
+ const existing = document.getElementById("execution-modal");
+ if (existing) existing.remove();
+
+ const overlay = document.createElement("div");
+ overlay.id = "execution-modal";
+ Object.assign(overlay.style, {
+ position: "fixed",
+ top: 0, left: 0,
+ width: "100vw",
+ height: "100vh",
+ background: "rgba(0,0,0,0.45)",
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ zIndex: "999999"
+ });
+
+ const box = document.createElement("div");
+ Object.assign(box.style, {
+ background: "#fff",
+ padding: "22px",
+ borderRadius: "12px",
+ width: "420px",
+ maxHeight: "80vh",
+ overflowY: "auto",
+ boxShadow: "0 4px 14px rgba(0,0,0,0.15)",
+ fontFamily: "Inter, sans-serif"
+ });
+
+ // Title
+ const title = document.createElement("h3");
+ title.textContent = result.status === "success" ? "Execution Summary" : "Execution Failed";
+ Object.assign(title.style, {
+ fontSize: "18px",
+ marginBottom: "10px",
+ fontWeight: "600",
+ });
+ box.appendChild(title);
+
+ // Message
+ const msg = document.createElement("p");
+ msg.textContent = result.message || "(no message)";
+ Object.assign(msg.style, { marginBottom: "12px", fontSize: "14px" });
+ box.appendChild(msg);
+
+ // Data block
+ if (result.data) {
+ const pre = document.createElement("pre");
+ pre.textContent = JSON.stringify(result.data, null, 2);
+ Object.assign(pre.style, {
+ background: "#1a202c",
+ color: "#f7fafc",
+ padding: "8px",
+ borderRadius: "6px",
+ fontSize: "11px",
+ whiteSpace: "pre-wrap",
+ overflowX: "auto",
+ marginBottom: "10px"
+ });
+ box.appendChild(pre);
+ }
+
+ // Steps list
+ if (Array.isArray(result.steps)) {
+ const stepsTitle = document.createElement("p");
+ stepsTitle.textContent = "Execution Steps:";
+ Object.assign(stepsTitle.style, { fontWeight: "600", marginTop: "10px" });
+ box.appendChild(stepsTitle);
+
+ const ul = document.createElement("ul");
+ result.steps.forEach(s => {
+ const li = document.createElement("li");
+ li.textContent = s;
+ ul.appendChild(li);
+ });
+ box.appendChild(ul);
+ }
+
+ // Close button
+ const close = document.createElement("button");
+ close.textContent = "Close";
+ Object.assign(close.style, {
+ marginTop: "16px",
+ width: "100%",
+ padding: "10px",
+ background: "#2b6cb0",
+ color: "#fff",
+ border: "none",
+ borderRadius: "8px",
+ cursor: "pointer",
+ fontWeight: "600"
+ });
+ close.onclick = () => overlay.remove();
+ box.appendChild(close);
+
+ overlay.appendChild(box);
+ document.body.appendChild(overlay);
+ }
+
+ // ========= workflow preview ===========
+ const renderWorkflow = (workflowArray, recipeTitle) => {
+ const container = document.createElement("div");
+ Object.assign(container.style, {
+ background: "#f9fafb",
+ border: "1px solid #e2e8f0",
+ borderRadius: "8px",
+ padding: "12px",
+ margin: "6px 6px 10px 6px",
+ fontSize: "12px",
+ color: "#374151",
+ });
+
+ // === Recipe title ===
+ const title = document.createElement("div");
+ title.textContent = recipeTitle || "Workflow";
+ Object.assign(title.style, {
+ fontWeight: "600",
+ fontSize: "14px",
+ marginBottom: "6px",
+ });
+ container.appendChild(title);
+
+ // 🔥 Store workflow metadata for backend context
+ collectedWorkflows.push({
+ recipe: recipeTitle,
+ workflow: workflowArray
+ });
+
+ // === Collapsible workflow body ===
+ const body = document.createElement("details");
+ body.className = "workflow-body";
+ body.open = false; // collapsed by default
+
+ const summary = document.createElement("summary");
+ summary.textContent = "Show workflow steps";
+ Object.assign(summary.style, {
+ cursor: "pointer",
+ color: "#2b6cb0",
+ fontWeight: "600",
+ marginBottom: "8px",
+ });
+ body.appendChild(summary);
+
+ const bodyContent = document.createElement("div");
+ Object.assign(bodyContent.style, {
+ paddingTop: "6px",
+ });
+
+ // === Steps ===
+ workflowArray.forEach((step, idx) => {
+ // === Step container (collapsible) ===
+ const stepBox = document.createElement("details");
+ stepBox.style.marginBottom = "6px";
+ stepBox.open = false; // collapsed by default
+
+// summary line
+ const stepSummary = document.createElement("summary");
+ stepSummary.textContent = `Step ${idx + 1}: ${step.tool || "Unnamed tool"}`;
+ Object.assign(stepSummary.style, {
+ cursor: "pointer",
+ fontWeight: "600",
+ color: "#2b6cb0",
+ });
+ stepBox.appendChild(stepSummary);
+
+// inner content — shown only when expanded
+ const inner = document.createElement("div");
+ Object.assign(inner.style, {
+ padding: "6px 0 0 4px",
+ display: "flex",
+ flexDirection: "column",
+ gap: "6px",
+ });
+
+// restore description
+ if (step.description) {
+ const desc = document.createElement("p");
+ desc.textContent = step.description;
+ desc.style.fontSize = "12px";
+ inner.appendChild(desc);
+ }
+
+// restore metadata
+ if (step.type || step.function) {
+ const meta = document.createElement("p");
+ meta.textContent = `${step.type || ""}${step.function ? " • " + step.function : ""}`;
+ Object.assign(meta.style, {
+ fontSize: "12px",
+ color: "#6b7280"
+ });
+ inner.appendChild(meta);
+ }
+
+// restore code block
+ if (step.code) {
+ const codeBlock = document.createElement("pre");
+ codeBlock.textContent = step.code;
+ Object.assign(codeBlock.style, {
+ background: "#1a202c",
+ color: "#f7fafc",
+ padding: "8px",
+ borderRadius: "6px",
+ fontSize: "11px",
+ overflowX: "auto",
+ whiteSpace: "pre-wrap"
+ });
+ inner.appendChild(codeBlock);
+ }
+
+ stepBox.appendChild(inner);
+
+ // Description
+ if (step.description) {
+ const desc = document.createElement("p");
+ desc.textContent = step.description;
+ Object.assign(desc.style, { marginBottom: "4px", fontSize: "12px" });
+ inner.appendChild(desc);
+ }
+
+ // Type / function metadata
+ if (step.type || step.function) {
+ const meta = document.createElement("p");
+ meta.style.fontSize = "12px";
+ meta.style.color = "#6b7280";
+ meta.textContent = `${step.type || ""} ${step.function ? "• " + step.function : ""}`;
+ inner.appendChild(meta);
+ }
+
+ // Code block
+ if (step.code) {
+ const codeBlock = document.createElement("pre");
+ codeBlock.textContent = step.code;
+ Object.assign(codeBlock.style, {
+ background: "#1a202c",
+ color: "#f7fafc",
+ padding: "8px",
+ borderRadius: "6px",
+ whiteSpace: "pre-wrap",
+ overflowX: "auto",
+ fontSize: "11px",
+ });
+ inner.appendChild(codeBlock);
+ }
+
+ // Wrapper for visible output zone
+ const wrapper = document.createElement("div");
+ wrapper.className = "step-wrapper";
+ wrapper.style.marginBottom = "8px";
+ wrapper.appendChild(stepBox);
+ bodyContent.appendChild(wrapper);
+ });
+
+ body.appendChild(bodyContent);
+ container.appendChild(body);
+
+ // === Execute button ===
+ const controls = document.createElement("div");
+ Object.assign(controls.style, {
+ marginTop: "10px",
+ display: "flex",
+ flexDirection: "column",
+ gap: "6px",
+ });
+
+ const mockBtn = document.createElement("button");
+ mockBtn.textContent = "Execute";
+ Object.assign(mockBtn.style, {
+ padding: "8px",
+ background: "#2f855a",
+ color: "#fff",
+ borderRadius: "6px",
+ border: "none",
+ cursor: "pointer",
+ });
+
+ // ----- Mock Execute Handler -----
+ mockBtn.onclick = async () => {
+ // addMsg("assistant", "🔄 Executing workflow…");
+
+ let result;
+ try {
+ const response = await fetch("https://api.thousandmonkeystypewriter.org/generate", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({query: recipeTitle}),
+ });
+
+ if (!response.ok) throw new Error("Backend execution failed: " + response.status);
+ result = await response.json();
+ } catch (err) {
+ addMsg("assistant", "❌ Execution error: " + err.message);
+ return;
+ }
+
+ // --- Now handle response ---
+ if (!result.status) {
+ addMsg("assistant", "⚠️ Unexpected backend response format.");
+ console.error(result);
+ return;
+ }
+
+ // 1) Show chat status message
+ if (result.status === "success") {
+ addMsg(
+ "assistant",
+ `🧪 Executed in simulation mode.
+ ${result.message}
+ To execute on-chain, provide these parameters:
+ ${(result.required || []).join(", ")}`
+ );
+ hideSuggestions(); // user is already in workflow mode
+ } else {
+ addMsg("assistant", `❌ Execution failed: ${result.message || "Unknown error"}`);
+ }
+
+ // 2) Show popup modal summary
+ showExecutionModal(result);
+ };
+
+ controls.appendChild(mockBtn);
+ container.appendChild(controls);
+ chatArea.appendChild(container);
+ chatArea.scrollTop = chatArea.scrollHeight;
+ };
+
+ // ========= main submit ===========
+ footer.onsubmit = async (e) => {
+ e.preventDefault();
+ const text = footerInput.value.trim();
+ if (!text) return;
+ footerInput.value = "";
+
+ if (placeholder.parentNode) placeholder.parentNode.removeChild(placeholder);
+
+ addMsg("user", text);
+
+ // Show "thinking" placeholder
+ const thinking = document.createElement("div");
+ thinking.textContent = "Thinking…";
+ Object.assign(thinking.style, {
+ padding: "8px 10px",
+ borderRadius: "10px",
+ background: "#f3f4f6",
+ maxWidth: "90%",
+ fontSize: "13px",
+ });
+ chatArea.appendChild(thinking);
+
+ const result = await sendToBackend(text);
+
+ thinking.remove();
+
+ // ===== Branching logic =====
+ if (result.mode === "error") {
+ addMsg("assistant", result.message);
+ return;
+ }
+
+ if (result.mode === "answer") {
+ addMsg("assistant", result.answer);
+ return;
+ }
+
+ if (result.mode === "workflow") {
+ addMsg("assistant", "⚙️ Executable workflow detected:");
+ renderWorkflow(result.workflow);
+ return;
+ }
+
+ if (result.mode === "mixed") {
+ if (result.answer) addMsg("assistant", result.answer);
+ if (result.workflow) {
+ // addMsg("assistant", "⚙️ Execution plan:");
+ renderWorkflow(result.workflow, result.recipe);
+ }
+ return;
+ }
+
+ // fallback
+ addMsg("assistant", "❓ Unexpected backend format.");
+ };
+ });
+})();
+
+
+