CGJI01_v0.2 / web /app.js
prashantmatlani's picture
updated - memory_store, persistent memory in api_server, app.js
2fe5352
// Front-end JavaScript - app.js
console.log("app.js loaded")
// Auto- fill and load session ID if exists (for now, just from global variable, can be extended to localStorage or URL param)
window.onload = () => {
if (window.session_id) {
document.getElementById("session_display").value = window.session_id
}
}
// -------------------------------
// GLOBAL STATE
// -------------------------------
window.conversationStarted = false
// Global memory store for agents
window.agentMemory = {}
// ---------------------------------------------------------
// INITIAL TRIGGER - Fire (Start Conversation; Run Analysis)
// ---------------------------------------------------------
//async function runAnalysis(){
//async function fire() {
window.fire = async function () {
//const inputBox = document.getElementById("initial_input")
const inputBox = document.getElementById("main_input")
//const text = inputBox.value
//console.log("πŸš€ Run Analysis:", text)
// safety check to prevent silent errrors if element isn't found
if (!inputBox) {
console.error("❌ main_input not found")
return
}
const text = inputBox.value
if (!text) return
// Create session once
if (!window.session_id) {
window.session_id = Math.random().toString(36).substring(7)
// πŸ”₯ Display it on UI
document.getElementById("session_display").value = window.session_id
}
// Mark conversation started
window.conversationStarted = true
// UI changes
switchToConversationMode()
// Show user message
appendMessage("user", text)
// Show "Thinking..."
showThinking()
inputBox.value = ""
// Disable Run Analysis permanently after first use
//document.querySelector('button[onclick="runAnalysis()"]').disabled = true
await sendToBackend(text)
}
// -----------------------------------------------------
// FUEL - FOLLOW-UP (Continue Conversation; Send button)
// -----------------------------------------------------
//async function sendFollowup(){
//async function fuel() {
window.fuel = async function () {
//const inputBox = document.getElementById("followup_input")
const inputBox = document.getElementById("main_input")
//const text = inputBox.value
if (!inputBox) {
console.error("❌ main_input not found")
return
}
const text = inputBox.value
//console.log("πŸ“¨ Follow-up:", text)
if (!text) return
// Show user message
appendMessage("user", text)
// Show "Thinking..."
showThinking()
inputBox.value = ""
await sendToBackend(text)
}
// -----------------
// UI Mode Switcher
// -----------------
function switchToConversationMode() {
const inputBox = document.getElementById("main_input")
// Change placeholder
inputBox.placeholder = "Continue..."
// Replace Fire β†’ Fuel
const fireBtn = document.getElementById("fire_btn")
const fuelBtn = document.createElement("button")
fuelBtn.innerText = "Fuel"
fuelBtn.id = "fuel_btn"
//fuelBtn.onclick = fuel
fuelBtn.onclick = window.fuel
fireBtn.replaceWith(fuelBtn)
// Show End Session button
document.getElementById("end_btn").style.display = "inline-block"
}
// --------------------------------------------------------------------------------------------------------
// LOAD SESSION - Allow loading previous session by ID (for now, no authentication, just input ID and load)
// --------------------------------------------------------------------------------------------------------
//function loadSession() {
async function loadSession() {
const input = document.getElementById("session_input").value
if (!input) return
window.session_id = input
const res = await fetch(`/session/${input}`)
const data = await res.json()
if (data.error) {
alert("Session not found")
return
}
// Set session display
document.getElementById("session_display").value = input
// Switch UI mode
window.conversationStarted = true
switchToConversationMode()
// Clear chat - Restore chat visually
const chatBox = document.getElementById("chat")
chatBox.innerHTML = ""
// Restore messages
data.history.forEach(msg => {
if (msg.role === "client") {
appendMessage("user", msg.content)
} else {
updateThinking(msg.content, "jung")
}
})
console.log("♻️ Session loaded:", input)
}
// -----------------------------------------------------------
// BACKEND CALL (SHARED) - PASS AGENT FROM BACKEND TO FRONTEND
// -----------------------------------------------------------
async function sendToBackend(text) {
const API_URL = window.location.origin + "/chat"
console.log("πŸ“‘ Sending to backend:", text)
try {
const res = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
session_id: window.session_id,
message: text
})
})
const data = await res.json()
//removeThinking()
// Format text (single line breaks)
const formatted = data.content.replace(/\n+/g, "<br>")
// Update thinking, prevents duplicate messages
//updateThinking(formatted)
updateThinking(data.content, data.agent)
// πŸ”₯ HANDLE CHOICE TYPE
if (data.type === "choice") {
renderChoices(data.choices)
}
// πŸ”₯ Store agent memory if present
if (data.agent_log) {
const agent = data.agent_log.agent
window.agentMemory[agent] = data.agent_log.history
updateAgentBox(agent, data.agent_log.history)
}
// 🧠 HANDLE EPISTEMIC (PASSIVE OBSERVER)
if (data.witness_updates) {
for (const agent in data.witness_updates) {
const content = data.witness_updates[agent]
// Epistemic is not conversational β†’ single block
updateAgentBox(agent, [
{
role: "agent",
text: content
}
])
}
}
} catch (err) {
console.error("❌ ERROR:", err)
removeThinking()
appendMessage("assistant", "Error: server not reachable")
}
}
// -------------------------------
// NEXT AGENT SELECTION
// -------------------------------
function renderChoices(choices) {
const chatBox = document.getElementById("chat")
const container = document.createElement("div")
container.className = "choice-container"
choices.forEach(choice => {
const btn = document.createElement("button")
btn.innerText = choice.label
btn.className = "choice-btn"
btn.onclick = () => {
handleAgentSelection(choice.id)
container.remove() // remove after click
}
container.appendChild(btn)
})
chatBox.appendChild(container)
chatBox.scrollTop = chatBox.scrollHeight
}
// -------------------------------
// HANDLE NEXT AGENT SELECTION
// -------------------------------
function handleAgentSelection(agentId) {
console.log("🧭 Selected agent:", agentId)
// Send agent selection as special message
sendToBackend(`__agent__:${agentId}`)
showThinking()
}
// -------------------------------
// SHOW "THINKING..."
// -------------------------------
function showThinking() {
const chatBox = document.getElementById("chat")
const thinking = document.createElement("div")
thinking.id = "thinking"
thinking.className = "ai-msg"
thinking.innerHTML = `
<span class="label-agent">Agent Jung:</span>
<br><br>
Thinking...
`
chatBox.appendChild(thinking)
}
// -------------------------------
// REPLACE "THINKING..." WITH RESPONSE
// -------------------------------
function updateThinking(text, agent = "jung") {
let thinking = document.getElementById("thinking")
const formatted = text.replace(/\n+/g, "<br>")
// Convert agent id β†’ display name
const agentName = formatAgentName(agent)
if (!thinking) {
const chatBox = document.getElementById("chat")
thinking = document.createElement("div")
thinking.className = "ai-msg"
chatBox.appendChild(thinking)
}
thinking.innerHTML = `
<span class="label-agent">Agent ${agentName}:</span>
<br><br>
${formatted}
`
thinking.id = ""
}
// -------------------------------
// REMOVE THINKING (fallback)
// -------------------------------
function removeThinking() {
const thinking = document.getElementById("thinking")
if (thinking) thinking.remove()
}
// -------------------------------
// RENDER USER MESSAGE ONLY
// -------------------------------
function appendMessage(role, text) {
const chatBox = document.getElementById("chat")
const msg = document.createElement("div")
if (role === "user") {
msg.innerHTML = `
<span class="label-client">Client:</span>
<br><br>
${text}
`
msg.className = "user-msg"
}
// ❗ IMPORTANT:
// We DO NOT render assistant here anymore
// Assistant is handled ONLY via updateThinking()
chatBox.appendChild(msg)
chatBox.scrollTop = chatBox.scrollHeight
}
// -------------------------------
// FORMAT AGENT NAME
// -------------------------------
function formatAgentName(agent) {
const map = {
jung: "Jung",
dream: "Dream",
shadow: "Shadow",
myth: "Myth",
epistemic: "Epistemic",
bpsy: "BPsy",
jred: "JRed",
synthesis: "Synthesis"
}
return map[agent] || agent
}
// -------------------------------
// UPDATE AGENT BOX
// -------------------------------
function updateAgentBox(agent, history) {
const box = document.getElementById(agent)
if (!box) return
let html = ""
history.forEach(entry => {
if (agent === "epistemic") {
html += formatEpistemic(entry.text)
} else {
if (entry.role === "client") {
html += `<b>Client:</b> ${entry.text}<br><br>`
} else {
html += `<b>Agent:</b> ${entry.text}<br><br>`
}
}
})
box.innerHTML = html
}
// -----------------------------------------------------
// HELPER FOR FORMATTING EPISTEMIC INSIGHTS WITH HEADERS
// -----------------------------------------------------
function formatEpistemic(text) {
return text
.replace("SUMMARY β€” CLIENT:", "<span class='insight-header'>Summary β€” Client</span>")
.replace("CROSS-ANALYSIS β€” CLIENT:", "<span class='insight-header'>Cross-Analysis β€” Client</span>")
.replace("SUMMARY β€” AGENT JUNG:", "<span class='insight-header'>Summary β€” Agent Jung</span>")
.replace("CROSS-ANALYSIS β€” AGENT JUNG:", "<span class='insight-header'>Cross-Analysis β€” Agent Jung</span>")
.replace(/\n/g, "<br>")
}
// ------------
// END SESSION
// ------------
async function endSession() {
console.log("πŸ›‘ Ending session")
const sessionId = window.session_id
await fetch(window.location.origin + "/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
session_id: sessionId,
message: "end session"
})
})
// πŸ”₯ POPUP MESSAGE
if (sessionId) {
alert(`Session ended.\n\nUse Session ID: ${sessionId} to resume later.`)
}
window.session_id = null
window.conversationStarted = false
document.getElementById("chat").innerHTML = ""
document.getElementById("session_display").value = ""
}
// ---------------------------------------------------------
// ENTER(SEND ACTION) & SHIFT+ENTER(INSERT NEW LINE) CONTROL
// ---------------------------------------------------------
window.addEventListener("DOMContentLoaded", () => {
const input = document.getElementById("main_input")
if (input) {
input.addEventListener("keydown", function (e) {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
if (window.conversationStarted) {
fuel()
} else {
fire()
}
}
})
}
})