diff --git a/src/backend/agents/group_chat_manager.py b/src/backend/agents/group_chat_manager.py index a418cc1ee..2b62b794e 100644 --- a/src/backend/agents/group_chat_manager.py +++ b/src/backend/agents/group_chat_manager.py @@ -2,6 +2,7 @@ import logging from datetime import datetime +import re from typing import Dict, List from autogen_core.base import AgentId, MessageContext @@ -239,14 +240,22 @@ async def _execute_step(self, session_id: str, step: Step): action=action_with_history, agent=step.agent, ) - logging.info(f"Sending ActionRequest to {step.agent.value.title()}") + logging.info(f"Sending ActionRequest to {step.agent.value}") + + if step.agent != "": + agent_name = step.agent.value + formatted_agent = re.sub( + r"([a-z])([A-Z])", r"\1 \2", agent_name + ) + else: + raise ValueError(f"Check {step.agent} is missing") await self._memory.add_item( AgentMessage( session_id=session_id, user_id=self._user_id, plan_id=step.plan_id, - content=f"Requesting {step.agent.value.title()} to perform action: {step.action}", + content=f"Requesting {formatted_agent} to perform action: {step.action}", source="GroupChatManager", step_id=step.id, ) diff --git a/src/frontend/wwwroot/app.css b/src/frontend/wwwroot/app.css index 88c2976e6..d5672fc03 100644 --- a/src/frontend/wwwroot/app.css +++ b/src/frontend/wwwroot/app.css @@ -4,191 +4,239 @@ /* App global */ -html { - overflow-y: auto; +html, +body { + overflow-x: hidden; + overflow-y: auto; + height: 100%; } body { - position: relative; - background: rgb(247, 249, 251); - min-height: 100vh; + position: relative; + background: rgb(247, 249, 251); + min-height: 100vh; } .border-right { - border-right: 1px solid hsl(221, 14%, calc(86% + 0%)); + border-right: 1px solid hsl(221, 14%, calc(86% + 0%)); } /* App template */ #app .columns { - min-height: 100vh; + min-height: 100vh; + height: 100%; +} +#app .modal, +#app .menu { + overflow: hidden; /* Prevent scrolling within modals and menus */ } - #app .asside { - background: rgba(231, 236, 243, 0.7); + background: rgba(231, 236, 243, 0.7); +} +ul#tasksStats.menu-list { + min-height: 100px; } - @media (min-width: 1800px) { - #app .asside { - max-width: 400px; - } + #app .asside { + max-width: 400px; + } } #app .menu-logo { - font-size: 1.25rem; - font-weight: 700; - cursor: pointer; + font-size: 1.25rem; + font-weight: 700; + cursor: pointer; } #app .menu-logo img { - width: 30px; + width: 30px; } #app .asside .menu-list a { - background-color: transparent; + background-color: transparent; } #app .asside .menu-list a.is-active { - background-color: rgb(71, 80, 235); + background-color: rgb(71, 80, 235); } #app .asside .menu-list a.is-active i { - color: white !important; + color: white !important; } #app .asside .menu-list a.is-active:hover { - background-color: rgb(71, 80, 235); + background-color: rgb(71, 80, 235); } #app .asside .menu-list a.menu-task { - display: flex; - align-items: center; + display: flex; + align-items: center; } #app .asside .menu-list a.menu-task span { - flex: 1; + flex: 1; } #app .asside .menu-list a:hover { - background-color: rgba(0, 0, 0, 0.1); + background-color: rgba(0, 0, 0, 0.1); } #app .iframe { - width: 100%; - background-color: transparent; - overflow-y: auto; + width: 100%; + background-color: transparent; } #app .context-switch { - position: fixed; - bottom: 50px; - right: calc(50% - 220px); - z-index: 3; + position: fixed; + bottom: 50px; + right: calc(50% - 220px); + z-index: 3; } .is-avatar.is-rounded { - border-radius: var(--bulma-radius-rounded); + border-radius: var(--bulma-radius-rounded); } .is-avatar.is-agent { - display: flex; - /* background-color: rgba(231, 236, 243, 0.7); */ - background-color: rgba(70, 79, 235, 0.25); + display: flex; + /* background-color: rgba(231, 236, 243, 0.7); */ + background-color: rgba(70, 79, 235, 0.25); } .is-avatar.is-agent img { - width: 75%; - height: 75%; - margin: 13%; + width: 75%; + height: 75%; + margin: 13%; } - - @keyframes moveImage { - 0% { - transform: rotate(0deg); - } + 0% { + transform: rotate(0deg); + } - 50% { - transform: rotate(-3deg); - } + 50% { + transform: rotate(-3deg); + } - 100% { - transform: rotate(3deg); - } + 100% { + transform: rotate(3deg); + } } .is-avatar.is-agent img.manager { - background-color: rgba(220, 56, 72, .35); - box-shadow: 0 0 0 4px rgba(220, 56, 72, .35); - animation: moveImage .3s infinite alternate; + background-color: rgba(220, 56, 72, 0.35); + box-shadow: 0 0 0 4px rgba(220, 56, 72, 0.35); + animation: moveImage 0.3s infinite alternate; } .is-avatar.is-agent img.hr_agent { - background-color: rgba(0, 209, 178, .35); - box-shadow: 0 0 0 4px rgba(0, 209, 178, .35); - animation: moveImage .5s infinite alternate; + background-color: rgba(0, 209, 178, 0.35); + box-shadow: 0 0 0 4px rgba(0, 209, 178, 0.35); + animation: moveImage 0.5s infinite alternate; } .is-avatar.is-agent img.procurement_agent { - background-color: rgba(255, 183, 15, .35); - box-shadow: 0 0 0 4px rgba(255, 183, 15, .35); - animation: moveImage .1s infinite alternate; + background-color: rgba(255, 183, 15, 0.35); + box-shadow: 0 0 0 4px rgba(255, 183, 15, 0.35); + animation: moveImage 0.1s infinite alternate; } .is-avatar.is-agent img.tech_agent { - background-color: rgba(178, 222, 39, .35); - box-shadow: 0 0 0 4px rgba(178, 222, 39, .35); - animation: moveImage .7s infinite alternate; + background-color: rgba(178, 222, 39, 0.35); + box-shadow: 0 0 0 4px rgba(178, 222, 39, 0.35); + animation: moveImage 0.7s infinite alternate; } .is-avatar.is-agent img.unknown { - background-color: rgba(39, 57, 222, 0.35); - box-shadow: 0 0 0 4px rgba(39, 57, 222, 0.35); - animation: moveImage .7s infinite alternate; + background-color: rgba(39, 57, 222, 0.35); + box-shadow: 0 0 0 4px rgba(39, 57, 222, 0.35); + animation: moveImage 0.7s infinite alternate; } .is-avatar.has-status::after { - content: ""; - position: absolute; - bottom: 0; - right: 0; - width: 30%; - height: 30%; - border-radius: 50%; - background-color: rgb(255, 255, 255); - border: 2px solid rgb(255, 255, 255); + content: ""; + position: absolute; + bottom: 0; + right: 0; + width: 30%; + height: 30%; + border-radius: 50%; + background-color: rgb(255, 255, 255); + border: 2px solid rgb(255, 255, 255); } .is-avatar.has-status.has-status-active::after { - background-color: hsl(var(--bulma-success-h), var(--bulma-success-s), var(--bulma-success-l)); + background-color: hsl( + var(--bulma-success-h), + var(--bulma-success-s), + var(--bulma-success-l) + ); } .is-avatar.has-status.has-status-busy::after { - background-color: hsl(var(--bulma-danger-h), var(--bulma-danger-s), var(--bulma-danger-l)); + background-color: hsl( + var(--bulma-danger-h), + var(--bulma-danger-s), + var(--bulma-danger-l) + ); } .is-avatar.has-status.has-status-paused::after { - background-color: hsl(var(--bulma-dark-h), var(--bulma-dark-s), var(--bulma-dark-l)); + background-color: hsl( + var(--bulma-dark-h), + var(--bulma-dark-s), + var(--bulma-dark-l) + ); } .button.is-greyed-out { - background-color: #e0e0e0; - color: lightgrey; - cursor: not-allowed; + background-color: #e0e0e0; + color: lightgrey; + cursor: not-allowed; } .button.is-selected { - background-color: #d3d3d3; - color: #000; + background-color: #d3d3d3; + color: #000; } - .notyf__toast { - max-width: 100% !important; - border-radius: var(--bulma-control-radius) !important; + max-width: 100% !important; + border-radius: var(--bulma-control-radius) !important; } .notyf__wrapper { - padding: 0.75rem 0.5rem !important; -} \ No newline at end of file + padding: 0.75rem 0.5rem !important; +} +/* Menu list scroll style start*/ +#app .asside .menu-list { + max-height: 300px; + overflow-y: scroll; + padding-right: 2px; + transition: all 0.3s ease; + box-sizing: border-box; +} +/* Hide the scrollbar initially (before hover) */ +#app .asside .menu-list::-webkit-scrollbar { + width: 8px; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0s 0.3s; +} +/* Style the scrollbar thumb (the draggable part) */ +#app .asside .menu-list::-webkit-scrollbar-thumb { + border-radius: 10px; + transition: background-color 0.3s ease; +} +/* Show the scrollbar and thumb when hovering */ +#app .asside .menu-list:hover::-webkit-scrollbar { + opacity: 1; + visibility: visible; + transition: opacity 0.3s ease, visibility 0s; +} +/* Style the thumb when hovering */ +#app .asside .menu-list:hover::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); +} +/* Menu list scroll style end*/ diff --git a/src/frontend/wwwroot/assets/Send.svg b/src/frontend/wwwroot/assets/Send.svg new file mode 100644 index 000000000..214d2ef72 --- /dev/null +++ b/src/frontend/wwwroot/assets/Send.svg @@ -0,0 +1,10 @@ + diff --git a/src/frontend/wwwroot/home/home.css b/src/frontend/wwwroot/home/home.css index e1999d5f4..f7c1b6b06 100644 --- a/src/frontend/wwwroot/home/home.css +++ b/src/frontend/wwwroot/home/home.css @@ -1,194 +1,260 @@ @import "../app.css"; .container { - inset: 0; - min-height: 100vh; - overflow-y: auto; + inset: 0; + min-height: 100vh; + overflow-y: auto; } .section { - min-width: 800px; - z-index: 1; + min-width: 800px; + z-index: 1; } .app-logo { - width: 60px; - margin: 0 auto; + width: 60px; + margin: 0 auto; } .background { - inset: 0; - position: absolute; - opacity: 0.1; - overflow-y: hidden; + inset: 0; + position: absolute; + opacity: 0.1; + overflow-y: hidden; } .description { - color: Black; - text-align: center; + color: Black; + text-align: center; - /* Web/Body 1 */ - font-family: "Segoe UI"; - font-size: 14px; - font-style: normal; - font-weight: 400; - line-height: 20px; /* 142.857% */ - margin-bottom: 10px; + /* Web/Body 1 */ + font-family: "Segoe UI"; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; /* 142.857% */ + margin-bottom: 10px; } .title { - color: var(--Light-Mode-Foreground-Neutral-Primary, var(--Foreground-Neutral-Primary, #111)); - text-align: center; - font-family: "Segoe UI"; - font-size: 32px; - font-style: normal; - font-weight: 700; - line-height: 40px; /* 125% */ + color: var( + --Light-Mode-Foreground-Neutral-Primary, + var(--Foreground-Neutral-Primary, #111) + ); + text-align: center; + font-family: "Segoe UI"; + font-size: 32px; + font-style: normal; + font-weight: 700; + line-height: 40px; /* 125% */ } .assistants { - text-align: center; - font-family: "Segoe UI"; - font-size: 32px; - font-style: normal; - font-weight: 700; - line-height: 40px; /* 125% */ + text-align: center; + font-family: "Segoe UI"; + font-size: 32px; + font-style: normal; + font-weight: 700; + line-height: 40px; /* 125% */ - background: var(--Gradient-M365-Chat-Light-Accessible, linear-gradient(90deg, #464FEB 10.42%, #8330E9 100%)); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; + background: var( + --Gradient-M365-Chat-Light-Accessible, + linear-gradient(90deg, #464feb 10.42%, #8330e9 100%) + ); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; } .orb { - width: 50%; - height: 50%; - border-radius: 50%; - filter: blur(50px); - background: radial-gradient(circle); - position: absolute; + width: 50%; + height: 50%; + border-radius: 50%; + filter: blur(50px); + background: radial-gradient(circle); + position: absolute; } .orb.one { - bottom: -40%; - left: 47.5%; - background-color: rgba(70, 79, 235, 1); + bottom: -40%; + left: 47.5%; + background-color: rgba(70, 79, 235, 1); } .orb.two { - bottom: -30%; - left: 25%; - background-color: rgb(18, 172, 149); - z-index: 1; + bottom: -30%; + left: 25%; + background-color: rgb(18, 172, 149); + z-index: 1; } .orb.three { - bottom: -40%; - left: 2.5%; - background-color: rgb(199, 20, 184); -} - -.new-task-control:has(>textarea:disabled)::before, -.new-task-control:has(>textarea:disabled)::after { - pointer-events: none; - content: ''; - position: absolute; - left: -2px; - top: -2px; - background: linear-gradient(45deg, #fb0094, #0000ff, #fb0094, #0000ff, #fb0094, #0000ff, #fb0094, #0000ff); - background-size: 400%; - width: calc(100% + 4px); - height: calc(100% + 4px); - border-radius: var(--bulma-input-radius); - opacity: 0.5; - z-index: -1; - animation: steam 20s linear infinite; + bottom: -40%; + left: 2.5%; + background-color: rgb(199, 20, 184); +} + +.new-task-control:has(> textarea:disabled)::before, +.new-task-control:has(> textarea:disabled)::after { + pointer-events: none; + content: ""; + position: absolute; + left: -2px; + top: -2px; + background: linear-gradient( + 45deg, + #fb0094, + #0000ff, + #fb0094, + #0000ff, + #fb0094, + #0000ff, + #fb0094, + #0000ff + ); + background-size: 400%; + width: calc(100% + 4px); + height: calc(100% + 4px); + border-radius: var(--bulma-input-radius); + opacity: 0.5; + z-index: -1; + animation: steam 20s linear infinite; } @keyframes steam { - 0% { - background-position: 0 0; - } + 0% { + background-position: 0 0; + } - 50% { - background-position: 400% 0; - } + 50% { + background-position: 400% 0; + } - 100% { - background-position: 0 0; - } + 100% { + background-position: 0 0; + } } .new-task-control::after { - filter: blur(25px); + filter: blur(25px); } .text-input-container { - width: 950px; - position: relative; - border: 1px solid #ccc; - border-radius: 8px; - background-color: white; - } + width: 950px; + position: relative; + border: 1px solid #ccc; + border-radius: 8px; + background-color: white; +} - textarea { - width: 98%; - padding: 0px; - border: none; - border-radius: 8px 8px 0 0; - font-size: 16px; - line-height: 1.5; - resize: none; - outline: none; - overflow: hidden; - margin: 0 10px; - align-items: center; - } +textarea { + width: 98%; + padding: 16px 0px 0px 0px; + border: none; + border-radius: 8px 8px 0 0; + font-size: 16px; + line-height: 1.5; + resize: none; + outline: none; + overflow: hidden; + margin: 0 10px; + align-items: center; + background-color: white; +} +textarea:disabled { + cursor: default; + background-color: white; +} - .middle-bar { - display: flex; - justify-content: space-between; - align-items: left; - padding: 0px 5px; - background-color: white; - } - - .bottom-bar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 3px 10px; - border-top: none; - border-bottom: 4px solid #0F6CBD; - background-color: white; +/*Spinner start*/ +#spinnerLoader { + display: flex; + flex-direction: column; + /* justify-content: center; */ + align-items: center; + position: absolute; + inset: 0; + color: black; + top: 30%; + left: 50%; + transform: translateX(-50%); + /* background-color: rgb(247, 249, 251);*/ + z-index: 9999; + font-weight: 500; +} + +#spinnerLoader span::before { + content: "Creating Tasks..."; + animation: spinLoaderAnimation infinite 3s linear; +} + +@keyframes spinLoaderAnimation { + 75% { + content: "Agents are on it..."; } +} + +#spinnerLoader i { + font-size: 3rem; +} + +#overlay { + position: fixed; + display: none; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.5); + + z-index: 1; +} + +/*Spinner end*/ + +.middle-bar { + display: flex; + justify-content: space-between; + align-items: left; + padding: 0px 5px; + background-color: white; +} + +.bottom-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 3px 10px; + border-top: none; + border-bottom: 4px solid #0f6cbd; + background-color: white; +} - .icons { - display: flex; - align-items: center; - } - - .star-icon { - margin-right: 10px; - cursor: pointer; - } - - .char-count { - font-size: 14px; - color: #888; - } - - .send-button { - border: none; - background: none; - font-size: 18px; - cursor: pointer; - color: #007bff; - padding: 4px; - outline: none; - } - - .send-button:hover { - color: #0056b3; - } + display: flex; + align-items: center; +} + +.star-icon { + margin-right: 10px; + cursor: pointer; +} + +.char-count { + font-size: 14px; + color: #888; +} + +.send-button { + border: none; + background: none; + font-size: 18px; + cursor: pointer; + color: #007bff; + padding: 4px; + outline: none; +} + +.send-button:hover { + color: #0056b3; +} diff --git a/src/frontend/wwwroot/home/home.js b/src/frontend/wwwroot/home/home.js index 29d96fbff..842ad0fc6 100644 --- a/src/frontend/wwwroot/home/home.js +++ b/src/frontend/wwwroot/home/home.js @@ -1,98 +1,207 @@ (() => { - const notyf = new Notyf({ position: { x: 'right', y: 'top' }, ripple: false, duration: 3000 }); - const apiEndpoint = sessionStorage.getItem('apiEndpoint'); - const newTaskPrompt = document.getElementById("newTaskPrompt"); - const startTaskButton = document.getElementById("startTaskButton"); - newTaskPrompt.focus(); - - - const startTask = () => { - - startTaskButton.addEventListener('click', (event) => { - - const sessionId = 'sid_' + (new Date()).getTime() + '_' + Math.floor(Math.random() * 10000); - - newTaskPrompt.disabled = true; - startTaskButton.disabled = true; - startTaskButton.classList.add('is-loading'); - - window.headers - .then(headers =>{ - fetch(apiEndpoint + '/input_task', { - method: 'POST', - headers: headers, - body: JSON.stringify({ - session_id: sessionId, - description: newTaskPrompt.value - }) - }) - .then(response => response.json()) - .then(data => { - - if (data.status == 'Plan not created') { - notyf.error('Unable to create plan for this task.'); - newTaskPrompt.disabled = false; - startTaskButton.disabled = false; - return; - } - - console.log('startTaskButton', data); - - newTaskPrompt.disabled = false; - startTaskButton.disabled = false; - startTaskButton.classList.remove('is-loading'); - - window.parent.postMessage({ - action: 'taskStarted', - session_id: data.session_id, - task_id: data.plan_id, - task_name: newTaskPrompt.value - }, '*'); - - newTaskPrompt.value = ''; - - notyf.success('Task created successfully. AI agents are on it!'); - - }) - .catch(error => { - console.error('Error:', error); - newTaskPrompt.disabled = false; - startTaskButton.disabled = false; - startTaskButton.classList.remove('is-loading'); - }) - }); - }) - }; - - const quickTasks = () => { - - document.querySelectorAll('.quick-task').forEach(task => { - task.addEventListener('click', (event) => { - const quickTaskPrompt = task.querySelector('.quick-task-prompt').innerHTML; - newTaskPrompt.value = quickTaskPrompt.trim().replace(/\s+/g, ' '); - }); - }); - + const notyf = new Notyf({ + position: { x: "right", y: "top" }, + ripple: false, + duration: 3000, + }); + const apiEndpoint = sessionStorage.getItem("apiEndpoint"); + const newTaskPrompt = document.getElementById("newTaskPrompt"); + const startTaskButton = document.getElementById("startTaskButton"); + const startTaskButtonContainer = document.querySelector(".send-button"); + const startTaskButtonImg = startTaskButtonContainer + ? startTaskButtonContainer.querySelector("img") + : null; + + newTaskPrompt.focus(); + + // Create spinner element + const createSpinner = () => { + if (!document.getElementById("spinnerContainer")) { + const spinnerContainer = document.createElement("div"); + spinnerContainer.id = "spinnerContainer"; + spinnerContainer.innerHTML = ` +
Status:
-
+ Status:
+
,
, etc.) wrap inside the message */
+.notification p,
+.notification pre {
+ margin: 0;
+ word-wrap: break-word;
+ white-space: pre-wrap; /* Allow preformatted text to wrap */
+}
+.message-content {
+ max-width: 100%;
+ overflow: hidden;
+ word-break: break-word;
+ line-height: 1.4;
+}
+/* Optional: Add word-breaking for URLs */
+.notification a {
+ word-wrap: break-word;
+ word-break: break-word;
+ text-decoration: underline;
+}
+/*Notification message styles end*/
.task-progress {
- height: 40vh;
- overflow-y: auto;
- background-color: white;
- border-radius: var(--bulma-radius);
+ height: 40vh;
+ overflow-y: auto;
+ background-color: white;
+ border-radius: var(--bulma-radius);
}
@media (min-height: 1200px) {
- .task-progress {
- height: 50vh;
- }
+ .task-progress {
+ height: 50vh;
+ }
}
@media (min-height: 1400px) {
- .task-progress {
- height: 60vh;
- }
+ .task-progress {
+ height: 60vh;
+ }
}
.task-progress .notification {
- display: inline-block;
- padding: 0.5rem 1rem;
+ padding: 0.5rem 1rem;
+ display: block;
+ max-width: 100%;
+ word-wrap: break-word;
+ box-sizing: border-box;
+ overflow-wrap: break-word;
}
.menu-list .menu-item,
.menu-list a,
.menu-list button {
- background-color: transparent;
+ background-color: transparent;
}
.menu-list ul.menu-stages {
- border-inline-start: 3px solid var(--bulma-border);
- padding-inline-start: 0;
+ border-inline-start: 3px solid var(--bulma-border);
+ padding-inline-start: 0;
}
.menu-list ul.menu-stages li {
- margin-left: calc(-1.4rem - 5px);
+ margin-left: calc(-1.4rem - 5px);
}
.menu-list a.menu-stage {
- display: flex;
- align-items: center;
- position: relative;
- padding: 0.5em 0 0.5rem 0.75em;
- width: calc(100% + 1.4rem - 5px);
+ display: flex;
+ align-items: center;
+ position: relative;
+ padding: 0.5em 0 0.5rem 0.75em;
+ width: calc(100% + 1.4rem - 5px);
}
-.menu-list a.menu-stage>i {
- font-size: 1.4rem;
- margin-top: 3px;
- border-radius: 50%;
- background-color: rgb(247, 249, 251);
- padding: 5px;
+.menu-list a.menu-stage > i {
+ font-size: 1.4rem;
+ margin-top: 3px;
+ border-radius: 50%;
+ background-color: rgb(247, 249, 251);
+ padding: 5px;
}
.menu-list a.menu-stage span {
- flex: 1;
+ flex: 1;
+ word-break: break-word; /*this for stages span alignment*/
}
.menu-list a.menu-stage.rejected span {
- text-decoration: line-through;
- opacity: 0.5;
+ text-decoration: line-through;
+ opacity: 0.5;
}
.menu-list a.menu-stage.action_requested span {
- font-weight: 500;
+ font-weight: 500;
}
.menu-list a.menu-stage div {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
.menu-stage-actions i {
- font-size: 1.4rem;
+ font-size: 1.4rem;
}
.business-animation {
- position: relative;
- border-radius: var(--bulma-radius-large);
+ position: relative;
+ border-radius: var(--bulma-radius-large);
}
#taskLoader {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- position: absolute;
- inset: 0;
- color: black;
- background-color: rgb(247, 249, 251);
- z-index: 1000;
- font-weight: 500;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ inset: 0;
+ color: black;
+ background-color: rgb(247, 249, 251);
+ z-index: 1000;
+ font-weight: 500;
}
#taskLoader span::before {
- content: "Getting task plan...";
- animation: taskLoaderAnimation infinite 3s linear;
+ content: "Getting task plan...";
+ animation: taskLoaderAnimation infinite 3s linear;
}
#taskLoader.is-hidden {
- display: none;
+ display: none;
}
@keyframes taskLoaderAnimation {
+ 0% {
+ content: "Getting task plan...";
+ }
- 0% {
- content: "Getting task plan...";
- }
-
- 50% {
- content: "Contacting agents...";
- }
+ 50% {
+ content: "Contacting agents...";
+ }
- 75% {
- content: "Loading conversations...";
- }
+ 75% {
+ content: "Loading conversations...";
+ }
}
#taskLoader i {
- font-size: 3rem;
+ font-size: 3rem;
}
.task-stage-divider {
- text-align: center;
- margin: 1rem 0;
- font-size: .85rem;
- font-weight: 500;
- border: 1px solid rgb(71, 80, 235);
- border-left-width: 0;
- border-right-width: 0;
- border-bottom-width: 0;
+ text-align: center;
+ margin: 1rem 0;
+ font-size: 0.85rem;
+ font-weight: 500;
+ border: 1px solid rgb(71, 80, 235);
+ border-left-width: 0;
+ border-right-width: 0;
+ border-bottom-width: 0;
}
.task-stage-divider legend {
- color: rgb(71, 80, 235);
- -webkit-padding-start: 1rem;
- -webkit-padding-end: 1rem;
- background: transparent;
+ color: rgb(71, 80, 235);
+ -webkit-padding-start: 1rem;
+ -webkit-padding-end: 1rem;
+ background: transparent;
}
.text-input-container {
- position: relative;
- border: 1px solid #ccc;
- border-radius: 8px;
- background-color: white;
+ position: relative;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ background-color: white;
}
-
+
textarea {
- width: 98%;
- padding: 0px;
- border: none;
- border-radius: 8px 8px 0 0;
- font-size: 16px;
- line-height: 1.5;
- resize: none;
- outline: none;
- overflow: hidden;
- margin: 0 10px;
- align-items: center;
+ width: 98%;
+ padding: 16px 0px 0px 0px;
+ border: none;
+ border-radius: 8px 8px 0 0;
+ font-size: 16px;
+ line-height: 1.5;
+ resize: none;
+ outline: none;
+ overflow: hidden;
+ margin: 0 10px;
+ align-items: center;
+ background-color: white;
}
.star-icon {
- margin-right: 10px;
- cursor: pointer;
+ margin-right: 10px;
+ cursor: pointer;
}
-
- .char-count {
- font-size: 14px;
- color: #888;
+
+.char-count {
+ font-size: 14px;
+ color: #888;
}
.middle-bar {
- display: flex;
- justify-content: space-between;
- align-items: left;
- padding: 0px 5px;
- background-color: white;
- }
-
- .bottom-bar {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 3px 10px;
- border-top: none;
- border-bottom: 4px solid #0F6CBD;
- background-color: white;
- }
-
- .send-button {
- border: none;
- background: none;
- font-size: 18px;
- cursor: pointer;
- color: #007bff;
- padding: 4px;
- outline: none;
-}
-
- .send-button:hover {
- color: #0056b3;
+ display: flex;
+ justify-content: space-between;
+ align-items: left;
+ padding: 0px 5px;
+ background-color: white;
+}
+
+.bottom-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 3px 10px;
+ border-top: none;
+ border-bottom: 4px solid #0f6cbd;
+ background-color: white;
+}
+
+.send-button {
+ border: none;
+ background: none;
+ font-size: 18px;
+ cursor: pointer;
+ color: #007bff;
+ padding: 4px;
+ outline: none;
+}
+
+.send-button:hover {
+ color: #0056b3;
}
diff --git a/src/frontend/wwwroot/task/task.js b/src/frontend/wwwroot/task/task.js
index 666d6deec..1282d5476 100644
--- a/src/frontend/wwwroot/task/task.js
+++ b/src/frontend/wwwroot/task/task.js
@@ -1,425 +1,469 @@
(() => {
- const markdownConverter = new showdown.Converter();
- const apiEndpoint = sessionStorage.getItem('apiEndpoint');
- const taskStore = JSON.parse(sessionStorage.getItem('task'));
- const taskName = document.getElementById("taskName");
- const taskStatusTag = document.getElementById("taskStatusTag");
- const taskStagesMenu = document.getElementById("taskStagesMenu");
- const taskPauseButton = document.getElementById("taskPauseButton");
- const taskAgentsButton = document.getElementById("taskAgentsButton");
- const taskWokFlowButton = document.getElementById("taskWokFlowButton");
- const taskMessageAddButton = document.getElementById("taskMessageAddButton");
- const taskMessages = document.getElementById("taskMessages");
- const taskDetailsAgents = document.getElementById("taskDetailsAgents");
- const taskProgress = document.getElementById("taskProgress");
- const taskProgressPercentage = document.getElementById("taskProgressPercentage");
- const taskProgressBar = document.getElementById("taskProgressBar");
- const taskLoader = document.getElementById("taskLoader");
- const taskAgentsHumans = document.getElementById("taskAgentsHumans");
- const taskStatusDetails = document.getElementById("taskStatusDetails");
- const taskCancelButton = document.getElementById("taskCancelButton");
-
- const notyf = new Notyf({
- position: { x: 'right', y: 'top' },
- ripple: false,
- duration: 3000,
- types: [
- {
- type: 'info',
- background: 'rgb(71, 80, 235)',
- icon: ''
- }
- ]
- });
-
- let taskSessionId = null;
- let taskLastStageId = null;
- let taskLastAction = null;
- let taskAgents = [];
- let taskAgentsVsHumans = [];
-
- const agentToIcon = (agentName) => {
- let agentIcon = '';
-
- switch (agentName) {
- case 'MarketingAgent':
- agentIcon = 'unknown';
- break;
- case 'HrAgent':
- agentIcon = 'hr_agent';
- break;
- case 'ExpenseBillingAgent':
- agentIcon = 'expense_billing_agent';
- break;
- case 'InvoiceReconciliationAgent':
- agentIcon = 'invoice_reconciliation_agent';
- break;
- case 'TechSupportAgent':
- agentIcon = 'tech_agent';
- break;
- case 'ProcurementAgent':
- agentIcon = 'procurement_agent';
- break;
- case 'ProductAgent':
- agentIcon = 'product_agent';
- break;
- case 'GroupChatManager':
- agentIcon = 'manager';
- break;
- case 'GenericAgent':
- agentIcon = 'manager';
- break;
- case 'HumanAgent':
- let userNumber = sessionStorage.getItem('userNumber');
- if (userNumber == null){
- // Generate a random number between 0 and 6
- userNumber = Math.floor(Math.random() * 6);
- // Create the icon name by concatenating 'user' with the random number
- sessionStorage.setItem('userNumber', userNumber);
- }
- let iconName = 'user' + userNumber;
- agentIcon = iconName;
- break;
- case 'Done':
- agentIcon = 'done';
- break;
- default:
- agentIcon = 'marketing_agent';
+ const markdownConverter = new showdown.Converter();
+ const apiEndpoint = sessionStorage.getItem("apiEndpoint");
+ const taskStore = JSON.parse(sessionStorage.getItem("task"));
+ const taskName = document.getElementById("taskName");
+ const taskStatusTag = document.getElementById("taskStatusTag");
+ const taskStagesMenu = document.getElementById("taskStagesMenu");
+ const taskPauseButton = document.getElementById("taskPauseButton");
+ const taskAgentsButton = document.getElementById("taskAgentsButton");
+ const taskWokFlowButton = document.getElementById("taskWokFlowButton");
+ const taskMessageAddButton = document.getElementById("taskMessageAddButton");
+ const taskMessages = document.getElementById("taskMessages");
+ const taskDetailsAgents = document.getElementById("taskDetailsAgents");
+ const taskProgress = document.getElementById("taskProgress");
+ const taskProgressPercentage = document.getElementById(
+ "taskProgressPercentage"
+ );
+ const taskProgressBar = document.getElementById("taskProgressBar");
+ const taskLoader = document.getElementById("taskLoader");
+ const taskAgentsHumans = document.getElementById("taskAgentsHumans");
+ const taskStatusDetails = document.getElementById("taskStatusDetails");
+ const taskCancelButton = document.getElementById("taskCancelButton");
+ const startTaskButtonContainer = document.querySelector(".send-button");
+ const startTaskButtonImg = startTaskButtonContainer
+ ? startTaskButtonContainer.querySelector("img")
+ : null;
+
+ const notyf = new Notyf({
+ position: { x: "right", y: "top" },
+ ripple: false,
+ duration: 3000,
+ types: [
+ {
+ type: "info",
+ background: "rgb(71, 80, 235)",
+ icon: '',
+ },
+ ],
+ });
+
+ const updateButtonImage = () => {
+ if (startTaskButtonImg) {
+ const newTaskPrompt = document.getElementById("taskMessageTextarea");
+ if (newTaskPrompt && newTaskPrompt.value.trim() === "") {
+ startTaskButtonImg.src = "../assets/images/air-button.svg";
+ startTaskButton.disabled = true;
+ } else {
+ startTaskButtonImg.src = "/assets/Send.svg";
+ startTaskButtonImg.style.width = "16px";
+ startTaskButtonImg.style.height = "16px";
+ startTaskButton.disabled = false;
+ }
+ }
+ };
+
+ let taskSessionId = null;
+ let taskLastStageId = null;
+ let taskLastAction = null;
+ let taskAgents = [];
+ let taskAgentsVsHumans = [];
+
+ const agentToIcon = (agentName) => {
+ let agentIcon = "";
+
+ switch (agentName) {
+ case "MarketingAgent":
+ agentIcon = "unknown";
+ break;
+ case "HrAgent":
+ agentIcon = "hr_agent";
+ break;
+ case "ExpenseBillingAgent":
+ agentIcon = "expense_billing_agent";
+ break;
+ case "InvoiceReconciliationAgent":
+ agentIcon = "invoice_reconciliation_agent";
+ break;
+ case "TechSupportAgent":
+ agentIcon = "tech_agent";
+ break;
+ case "ProcurementAgent":
+ agentIcon = "procurement_agent";
+ break;
+ case "ProductAgent":
+ agentIcon = "product_agent";
+ break;
+ case "GroupChatManager":
+ agentIcon = "manager";
+ break;
+ case "GenericAgent":
+ agentIcon = "manager";
+ break;
+ case "HumanAgent":
+ let userNumber = sessionStorage.getItem("userNumber");
+ if (userNumber == null) {
+ // Generate a random number between 0 and 6
+ userNumber = Math.floor(Math.random() * 6);
+ // Create the icon name by concatenating 'user' with the random number
+ sessionStorage.setItem("userNumber", userNumber);
}
-
- return `
`;
+ let iconName = "user" + userNumber;
+ agentIcon = iconName;
+ break;
+ case "Done":
+ agentIcon = "done";
+ break;
+ default:
+ agentIcon = "marketing_agent";
}
-
- const toDateTime = (timestamp) => {
- const date = new Date(timestamp * 1000);
- const options = { month: 'short', day: 'numeric' };
- const timeOptions = { hour: 'numeric', minute: 'numeric', hour12: true };
- return `${date.toLocaleDateString('en-US', options)} at ${date.toLocaleTimeString('en-US', timeOptions)}`;
- };
-
- const removeClassesExcept = (element, classToKeep) => {
- element.className = classToKeep;
+ return `
`;
+ };
+
+ const toDateTime = (timestamp) => {
+ const date = new Date(timestamp * 1000);
+ const options = { month: "short", day: "numeric" };
+ const timeOptions = { hour: "numeric", minute: "numeric", hour12: true };
+ return `${date.toLocaleDateString(
+ "en-US",
+ options
+ )} at ${date.toLocaleTimeString("en-US", timeOptions)}`;
+ };
+
+ const removeClassesExcept = (element, classToKeep) => {
+ element.className = classToKeep;
+ };
+
+ const handleDisableOfActions = (status) => {
+ if(status === "completed"){
+ taskPauseButton.disabled=true;
+ taskCancelButton.disabled=true;
+ } else {
+ taskPauseButton.disabled=false;
+ taskCancelButton.disabled=false;
}
-
- const taskHeaderActions = () => {
-
- if (taskPauseButton) {
- taskPauseButton.addEventListener('click', (event) => {
- const iconElement = taskPauseButton.querySelector('i');
- if (iconElement) {
- iconElement.classList.toggle('fa-circle-pause');
- iconElement.classList.toggle('fa-circle-play');
- if (iconElement.classList.contains('fa-circle-play')) {
- taskPauseButton.classList.add('has-text-success');
- removeClassesExcept(taskStatusTag, 'tag');
- taskStatusTag.classList.add('is-warning');
- taskStatusTag.textContent = 'Paused';
- } else {
- taskPauseButton.classList.remove('has-text-success');
- removeClassesExcept(taskStatusTag, 'tag');
- taskStatusTag.classList.add('is-info');
- taskStatusTag.textContent = 'Restarting';
- taskDetails();
- }
- }
- });
+ }
+
+ const taskHeaderActions = () => {
+ if (taskPauseButton) {
+ taskPauseButton.addEventListener("click", (event) => {
+ const iconElement = taskPauseButton.querySelector("i");
+ if (iconElement) {
+ iconElement.classList.toggle("fa-circle-pause");
+ iconElement.classList.toggle("fa-circle-play");
+ if (iconElement.classList.contains("fa-circle-play")) {
+ taskPauseButton.classList.add("has-text-success");
+ removeClassesExcept(taskStatusTag, "tag");
+ taskStatusTag.classList.add("is-warning");
+ taskStatusTag.textContent = "Paused";
+ } else {
+ taskPauseButton.classList.remove("has-text-success");
+ removeClassesExcept(taskStatusTag, "tag");
+ taskStatusTag.classList.add("is-info");
+ taskStatusTag.textContent = "Restarting";
+ taskDetails();
+ }
}
-
- if (taskCancelButton) {
- taskCancelButton.addEventListener('click', (event) => {
-
- const apiTaskStore = JSON.parse(sessionStorage.getItem('apiTask'));
- actionStages(apiTaskStore, false);
-
- });
- }
-
+ });
}
- const updateTaskDetailsAgents = (agents) => {
-
- taskDetailsAgents.innerHTML = '';
- taskAgentsVsHumans = [];
+ if (taskCancelButton) {
+ taskCancelButton.addEventListener("click", (event) => {
+ const apiTaskStore = JSON.parse(sessionStorage.getItem("apiTask"));
+ handleDisableOfActions("completed")
+ actionStages(apiTaskStore, false);
+ });
+ }
+ };
- agents.forEach(agent => {
+ const updateTaskDetailsAgents = (agents) => {
+ taskDetailsAgents.innerHTML = "";
+ taskAgentsVsHumans = [];
- const isAvatar = (agent === 'HumanAgent') ? 'is-human' : 'is-avatar'
+ agents.forEach((agent) => {
+ const isAvatar = agent === "HumanAgent" ? "is-human" : "is-avatar";
- taskDetailsAgents.innerHTML += `
+ taskDetailsAgents.innerHTML += `
${agentToIcon(agent)}
`;
- (agent === 'HumanAgent') ? taskAgentsVsHumans.push('Human') : taskAgentsVsHumans.push('Agent');
-
- })
-
- const humansInv = taskAgentsVsHumans.filter(agent => agent === 'Human');
- const humanInvText = humansInv.length > 1 ? 'humans' : 'human';
+ agent === "HumanAgent"
+ ? taskAgentsVsHumans.push("Human")
+ : taskAgentsVsHumans.push("Agent");
+ });
- const agentsInv = taskAgentsVsHumans.filter(agent => agent === 'Agent');
- const agentsInvText = agentsInv.length > 1 ? 'agents' : 'agent';
+ const humansInv = taskAgentsVsHumans.filter((agent) => agent === "Human");
+ const humanInvText = humansInv.length > 1 ? "humans" : "human";
+ const agentsInv = taskAgentsVsHumans.filter((agent) => agent === "Agent");
+ const agentsInvText = agentsInv.length > 1 ? "agents" : "agent";
- taskAgentsHumans.innerHTML = `Team selected: ${humansInv.length} ${humanInvText}, ${agentsInv.length} ${agentsInvText}`;
- }
+ taskAgentsHumans.innerHTML = `Team selected: ${humansInv.length} ${humanInvText}, ${agentsInv.length} ${agentsInvText}`;
+ };
- const updateTaskStatusDetails = (task) => {
+ const updateTaskStatusDetails = (task) => {
+ taskStatusDetails.innerHTML = "";
- taskStatusDetails.innerHTML = '';
-
- taskStatusDetails.innerHTML = `
+ taskStatusDetails.innerHTML = `
Summary: ${task.summary}
Created: ${toDateTime(task.ts)}
`;
+ };
+
+ const fetchPlanDetails = async (session_id) => {
+ console.log("/plans?session_id:", window.headers);
+
+ const headers = await window.headers;
+
+ return fetch(apiEndpoint + "/plans?session_id=" + session_id, {
+ method: "GET",
+ headers: headers,
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ console.log("fetchPlanDetails", data[0]);
+
+ updateTaskStatusDetails(data[0]);
+ updateTaskProgress(data[0]);
+ fetchTaskStages(data[0]);
+
+ sessionStorage.setItem("apiTask", JSON.stringify(data[0]));
+ })
+ .catch((error) => {
+ console.error("Error:", error);
+ });
+ };
+
+ const fetchTaskStages = (task) => {
+ window.headers.then((headers) => {
+ fetch(apiEndpoint + "/steps/" + task.id, {
+ method: "GET",
+ headers: headers,
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ console.log("fetchTaskStages", data);
+
+ if (taskStagesMenu) taskStagesMenu.innerHTML = "";
+ let taskStageCount = 0;
+ let taskStageApprovalStatus = 0;
+
+ if (data && data.length > 0) {
+ taskAgents = [];
+
+ data.forEach((stage) => {
+ const stageItem = document.createElement("li");
+ const stageBase64 = btoa(
+ encodeURIComponent(JSON.stringify(stage))
+ );
+
+ let stageStatusIcon = "";
+ let stageActions = "";
+ let stageRejected = "";
+
+ switch (stage.status) {
+ case "planned":
+ stageStatusIcon = ``;
+ break;
+ case "awaiting_feedback":
+ stageStatusIcon = ``;
+ break;
+ case "approved":
+ stageStatusIcon = ``;
+ break;
+ case "rejected":
+ stageStatusIcon = ``;
+ break;
+ case "action_requested":
+ stageStatusIcon = ``;
+ break;
+ case "completed":
+ stageStatusIcon = ``;
+ break;
+ case "failed":
+ stageStatusIcon = ``;
+ break;
+ default:
+ stageStatusIcon = ``;
+ }
+
+ if (stage.human_approval_status === "rejected") {
+ stageRejected = "rejected";
+ stageStatusIcon = ``;
+ }
+
+ if (stage.human_approval_status === "requested")
+ stageActions = `
+
+ `;
- }
+ stageItem.innerHTML = `
+
+ ${stageStatusIcon}
+ ${taskStageCount + 1}. ${
+ stage.action
+ }
+ ${stageActions}
+
+ `;
- const fetchPlanDetails = async (session_id) => {
+ if (taskStagesMenu) taskStagesMenu.appendChild(stageItem);
- console.log("/plans?session_id:", window.headers);
+ taskSessionId = stage.session_id;
+ taskLastStageId = stage.id;
+ taskLastAction = stage.action;
+ taskAgents.push(stage.agent);
- const headers = await window.headers
+ stageItem
+ .querySelectorAll(".menu-stage-action")
+ .forEach((action) => {
+ action.addEventListener("click", (event) => {
+ actionStage(
+ event.target.dataset.action,
+ event.target.dataset.stage
+ );
- return fetch(apiEndpoint + '/plans?session_id=' + session_id, {
- method: 'GET',
- headers: headers,
- })
- .then(response => response.json())
- .then(data => {
+ action.parentElement.style.display = "none";
+ });
+ });
- console.log('fetchPlanDetails', data[0]);
+ if (stage.human_approval_status === "requested")
+ taskStageApprovalStatus++;
- updateTaskStatusDetails(data[0]);
- updateTaskProgress(data[0]);
- fetchTaskStages(data[0]);
+ taskStageCount++;
+ });
- sessionStorage.setItem('apiTask', JSON.stringify(data[0]));
+ updateTaskDetailsAgents([...new Set(taskAgents)]);
- })
- .catch(error => {
- console.error('Error:', error);
- })
- }
+ sessionStorage.setItem("showApproveAll", false);
- const fetchTaskStages = (task) => {
-
- window.headers
- .then(headers => {
- fetch(apiEndpoint + '/steps/' + task.id, {
- method: 'GET',
- headers: headers,
- })
- .then(response => response.json())
- .then(data => {
-
- console.log('fetchTaskStages', data);
-
- if (taskStagesMenu) taskStagesMenu.innerHTML = '';
- let taskStageCount = 0;
- let taskStageApprovalStatus = 0;
-
- if (data && data.length > 0) {
-
- taskAgents = [];
-
- data.forEach(stage => {
- const stageItem = document.createElement('li');
- const stageBase64 = btoa(encodeURIComponent(JSON.stringify(stage)));
-
- let stageStatusIcon = '';
- let stageActions = '';
- let stageRejected = '';
-
- switch (stage.status) {
- case 'planned':
- stageStatusIcon = ``;
- break;
- case 'awaiting_feedback':
- stageStatusIcon = ``;
- break;
- case 'approved':
- stageStatusIcon = ``;
- break;
- case 'rejected':
- stageStatusIcon = ``;
- break;
- case 'action_requested':
- stageStatusIcon = ``;
- break;
- case 'completed':
- stageStatusIcon = ``;
- break;
- case 'failed':
- stageStatusIcon = ``;
- break;
- default:
- stageStatusIcon = ``;
- }
-
- if (stage.human_approval_status === 'rejected') {
- stageRejected = 'rejected';
- stageStatusIcon = ``;
- }
-
- if (stage.human_approval_status === 'requested') stageActions = `
-
- `;
-
- stageItem.innerHTML = `
-
- ${stageStatusIcon}
- ${taskStageCount + 1}. ${stage.action}
- ${stageActions}
-
- `;
-
- if (taskStagesMenu) taskStagesMenu.appendChild(stageItem);
-
- taskSessionId = stage.session_id;
- taskLastStageId = stage.id;
- taskLastAction = stage.action;
- taskAgents.push(stage.agent);
-
- stageItem.querySelectorAll('.menu-stage-action').forEach(action => {
- action.addEventListener('click', (event) => {
-
- actionStage(event.target.dataset.action, event.target.dataset.stage);
-
- action.parentElement.style.display = 'none';
-
- });
- });
-
- if (stage.human_approval_status === 'requested') taskStageApprovalStatus++;
-
- taskStageCount++;
-
- })
-
- updateTaskDetailsAgents([...new Set(taskAgents)]);
-
- sessionStorage.setItem('showApproveAll', false);
-
- // Feature approve all removed for this version
- // if (isHumanFeedbackPending()) {
- // sessionStorage.setItem('showApproveAll', false);
- // console.log('showApproveAll status', "showApproveAll is false");
-
- // } else {
- // sessionStorage.setItem('showApproveAll', taskStageApprovalStatus === taskStageCount);
- // console.log('showApproveAll status', taskStageApprovalStatus === taskStageCount);
-
- // }
- }
-
- fetchTaskMessages(task);
-
- window.parent.postMessage({
- action: 'taskStarted',
- }, '*');
-
- })
- .catch(error => {
- console.error('Error:', error);
- })
- })
- }
+ // Feature approve all removed for this version
+ // if (isHumanFeedbackPending()) {
+ // sessionStorage.setItem('showApproveAll', false);
+ // console.log('showApproveAll status', "showApproveAll is false");
+
+ // } else {
+ // sessionStorage.setItem('showApproveAll', taskStageApprovalStatus === taskStageCount);
+ // console.log('showApproveAll status', taskStageApprovalStatus === taskStageCount);
- const fetchTaskMessages = (task) => {
- window.headers
- .then(headers => {
- fetch(apiEndpoint + '/agent_messages/' + task.session_id, {
- method: 'GET',
- headers: headers,
- })
- .then(response => response.json())
- .then(data => {
-
- console.log('fetchTaskMessages', data);
-
- const toAgentName = (str) => {
- return str.replace(/([a-z])([A-Z])/g, '$1 $2');
- };
-
- const groupByStepId = (messages) => {
- const groupedMessages = {};
-
- messages.forEach(message => {
- const stepId = message.step_id || 'planner';
- if (!groupedMessages[stepId]) {
- groupedMessages[stepId] = [];
- }
- groupedMessages[stepId].push(message);
- });
-
- return groupedMessages;
- }
-
- const contextFilter = (messages) => {
- const filteredMessages = [];
-
- messages.forEach(message => {
- if (message.source !== 'PlannerAgent' && message.source !== 'GroupChatManager') {
- filteredMessages.push(message);
- }
- });
-
- return filteredMessages;
- }
-
- taskMessages.innerHTML = '';
-
- // console.log(groupByStepId(data));
-
- if (sessionStorage.getItem('context') && sessionStorage.getItem('context') === 'customer') {
-
- console.log('contextFilter', contextFilter(data));
-
- data = contextFilter(data);
-
- }
-
- if (data) {
-
- let stageCount = 0;
- let messageCount = 1;
- const groupedData = groupByStepId(data);
-
- Object.keys(groupedData).forEach(stage => {
-
- const messages = groupedData[stage];
- const messageGroupItem = document.createElement('fieldset');
-
- messageGroupItem.classList.add('task-stage-divider');
- messageGroupItem.classList.add('has-text-info');
-
- messageGroupItem.innerHTML = (stageCount === 0) ? '' : ``;
- taskMessages.appendChild(messageGroupItem);
-
- messages.forEach(message => {
- const messageItem = document.createElement('div');
- const showApproveAll = sessionStorage.getItem('showApproveAll') === 'true' && data.length === messageCount;
-
- let approveAllStagesButton = '';
-
- messageItem.classList.add('media');
- const isAvatar = (message.source === 'HumanAgent') ? 'is-human' : 'is-avatar'
- const isActive = (message.source === 'PlannerAgent') ? 'has-status-busy' : 'has-status-active'
-
- if (sessionStorage.getItem('context') && sessionStorage.getItem('context') !== 'customer') {
- if (showApproveAll) {
- console.log('Creating approveAllStagesButton');
- approveAllStagesButton = `If you are happy with the plan, you can approve all stages.
`;
-
- }
- }
- const messageLeft = `
+ // }
+ }
+
+ fetchTaskMessages(task);
+
+ window.parent.postMessage(
+ {
+ action: "taskStarted",
+ },
+ "*"
+ );
+ })
+ .catch((error) => {
+ console.error("Error:", error);
+ });
+ });
+ };
+
+ const fetchTaskMessages = (task) => {
+ window.headers.then((headers) => {
+ fetch(apiEndpoint + "/agent_messages/" + task.session_id, {
+ method: "GET",
+ headers: headers,
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ console.log("fetchTaskMessages", data);
+
+ const toAgentName = (str) => {
+ return str.replace(/([a-z])([A-Z])/g, "$1 $2");
+ };
+
+ const groupByStepId = (messages) => {
+ const groupedMessages = {};
+
+ messages.forEach((message) => {
+ const stepId = message.step_id || "planner";
+ if (!groupedMessages[stepId]) {
+ groupedMessages[stepId] = [];
+ }
+ groupedMessages[stepId].push(message);
+ });
+
+ return groupedMessages;
+ };
+
+ const contextFilter = (messages) => {
+ const filteredMessages = [];
+
+ messages.forEach((message) => {
+ if (
+ message.source !== "PlannerAgent" &&
+ message.source !== "GroupChatManager"
+ ) {
+ filteredMessages.push(message);
+ }
+ });
+
+ return filteredMessages;
+ };
+
+ taskMessages.innerHTML = "";
+
+ // console.log(groupByStepId(data));
+
+ if (
+ sessionStorage.getItem("context") &&
+ sessionStorage.getItem("context") === "customer"
+ ) {
+ console.log("contextFilter", contextFilter(data));
+
+ data = contextFilter(data);
+ }
+
+ if (data) {
+ let stageCount = 0;
+ let messageCount = 1;
+ const groupedData = groupByStepId(data);
+
+ Object.keys(groupedData).forEach((stage) => {
+ const messages = groupedData[stage];
+ const messageGroupItem = document.createElement("fieldset");
+
+ messageGroupItem.classList.add("task-stage-divider");
+ messageGroupItem.classList.add("has-text-info");
+
+ messageGroupItem.innerHTML =
+ stageCount === 0
+ ? ""
+ : ``;
+ taskMessages.appendChild(messageGroupItem);
+
+ messages.forEach((message) => {
+ const messageItem = document.createElement("div");
+ const showApproveAll =
+ sessionStorage.getItem("showApproveAll") === "true" &&
+ data.length === messageCount;
+
+ let approveAllStagesButton = "";
+
+ messageItem.classList.add("media");
+ const isAvatar =
+ message.source === "HumanAgent" ? "is-human" : "is-avatar";
+ const isActive =
+ message.source === "PlannerAgent"
+ ? "has-status-busy"
+ : "has-status-active";
+
+ if (
+ sessionStorage.getItem("context") &&
+ sessionStorage.getItem("context") !== "customer"
+ ) {
+ if (showApproveAll) {
+ console.log("Creating approveAllStagesButton");
+ approveAllStagesButton = `If you are happy with the plan, you can approve all stages.
`;
+ }
+ }
+ const messageLeft = `
@@ -429,22 +473,30 @@
- ${toAgentName(message.source)} • ${toDateTime(message.ts)} AI-generated content may be incorrect
+ ${toAgentName(
+ message.source
+ )} • ${toDateTime(
+ message.ts
+ )} AI-generated content may be incorrect
- ${markdownConverter.makeHtml(message.content)} ${approveAllStagesButton}
+ ${markdownConverter.makeHtml(
+ message.content
+ )} ${approveAllStagesButton}
- `
- const messageRight = `
+ `;
+ const messageRight = `
You • ${toDateTime(message.ts)}
- ${markdownConverter.makeHtml(message.content)}
+ ${markdownConverter.makeHtml(
+ message.content
+ )}
@@ -454,318 +506,354 @@
${agentToIcon(message.source)}
- `
-
- const messageTemplate = (message.source === 'HumanAgent') ? messageRight : messageLeft;
- messageItem.innerHTML = messageTemplate;
- taskMessages.appendChild(messageItem);
-
- if (sessionStorage.getItem('context') && sessionStorage.getItem('context') !== 'customer') {
- if (showApproveAll) {
-
- document.getElementById("approveAllStagesButton").addEventListener('click', (event) => actionStages(task, true));
-
- };
- };
-
- messageCount++;
-
- });
-
- stageCount++;
-
- });
-
- const mediaContents = document.querySelectorAll('.media-content');
- if (mediaContents.length > 0) {
- mediaContents[mediaContents.length - 1].scrollIntoView({ behavior: 'smooth' });
- }
-
- if (sessionStorage.getItem('context') && sessionStorage.getItem('context') === 'customer' && !sessionStorage.getItem('actionStagesRun').includes(task.session_id)) {
-
-
- actionStages(task, true);
-
- let actionStagesRun = JSON.parse(sessionStorage.getItem('actionStagesRun') || '[]');
-
- actionStagesRun.push(task.session_id);
- sessionStorage.setItem('actionStagesRun', JSON.stringify(actionStagesRun));
- }
-
- setTimeout(() => { taskLoader.classList.add('is-hidden'); }, 500);
-
- }
-
- })
- .catch(error => {
- console.error('Error:', error);
- })
+ `;
+
+ const messageTemplate =
+ message.source === "HumanAgent" ? messageRight : messageLeft;
+ messageItem.innerHTML = messageTemplate;
+ taskMessages.appendChild(messageItem);
+
+ if (
+ sessionStorage.getItem("context") &&
+ sessionStorage.getItem("context") !== "customer"
+ ) {
+ if (showApproveAll) {
+ document
+ .getElementById("approveAllStagesButton")
+ .addEventListener("click", (event) =>
+ actionStages(task, true)
+ );
+ }
+ }
+
+ messageCount++;
+ });
+
+ stageCount++;
});
-
- }
+ const mediaContents = document.querySelectorAll(".media-content");
+ if (mediaContents.length > 0) {
+ mediaContents[mediaContents.length - 1].scrollIntoView({
+ behavior: "smooth",
+ });
+ }
- const updateTaskProgress = (task) => {
+ if (
+ sessionStorage.getItem("context") &&
+ sessionStorage.getItem("context") === "customer" &&
+ !sessionStorage
+ .getItem("actionStagesRun")
+ .includes(task.session_id)
+ ) {
+ actionStages(task, true);
+
+ let actionStagesRun = JSON.parse(
+ sessionStorage.getItem("actionStagesRun") || "[]"
+ );
+
+ actionStagesRun.push(task.session_id);
+ sessionStorage.setItem(
+ "actionStagesRun",
+ JSON.stringify(actionStagesRun)
+ );
+ }
- const taskStatusToLabel = (str) => {
- return str.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
- };
+ setTimeout(() => {
+ taskLoader.classList.add("is-hidden");
+ }, 500);
+ }
+ })
+ .catch((error) => {
+ console.error("Error:", error);
+ });
+ });
+ };
+
+ const updateTaskProgress = (task) => {
+ const taskStatusToLabel = (str) => {
+ return str
+ .split("_")
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(" ");
+ };
- const totalSteps = task.total_steps;
- const completedSteps = task.completed;
- const approvalRequired = task.steps_requiring_approval;
+ const totalSteps = task.total_steps;
+ const completedSteps = task.completed;
+ const approvalRequired = task.steps_requiring_approval;
- const percentage = (completedSteps / totalSteps) * 100;
- const progressString = `Progress ${completedSteps}/${totalSteps}`;
- const percentageString = `${percentage.toFixed(0)}%`;
+ const percentage = (completedSteps / totalSteps) * 100;
+ const progressString = `Progress ${completedSteps}/${totalSteps}`;
+ const percentageString = `${percentage.toFixed(0)}%`;
- taskProgress.textContent = progressString;
- taskProgressPercentage.textContent = percentageString;
- taskProgressBar.value = parseFloat(percentageString);
+ taskProgress.textContent = progressString;
+ taskProgressPercentage.textContent = percentageString;
+ taskProgressBar.value = parseFloat(percentageString);
- taskStatusTag.textContent = taskStatusToLabel(task.overall_status);
+ taskStatusTag.textContent = taskStatusToLabel(task.overall_status);
- if (task.overall_status === 'completed') {
- removeClassesExcept(taskStatusTag, 'tag');
- taskStatusTag.classList.add('is-success');
- }
+ if (task.overall_status === "completed") {
+ removeClassesExcept(taskStatusTag, "tag");
+ taskStatusTag.classList.add("is-success");
+ } else if (task.overall_status === "in_progress") {
+ removeClassesExcept(taskStatusTag, "tag");
+ taskStatusTag.classList.add("is-info");
+ }
+ handleDisableOfActions(task.overall_status)
+ };
+
+ const isHumanFeedbackPending = () => {
+ const storedData = sessionStorage.getItem("apiTask");
+ const planDetails = JSON.parse(storedData);
+ return (
+ planDetails.human_clarification_request !== null &&
+ planDetails.human_clarification_response === null
+ );
+ };
+
+ const actionStage = (action, stage) => {
+ if (isHumanFeedbackPending()) {
+ notyf.error("You must first provide feedback to the planner.");
+ return;
}
- const isHumanFeedbackPending = () => {
- const storedData = sessionStorage.getItem('apiTask');
- const planDetails = JSON.parse(storedData);
- return planDetails.human_clarification_request !== null && planDetails.human_clarification_response === null;
- };
-
- const actionStage = (action, stage) => {
-
- if (isHumanFeedbackPending()) {
- notyf.error('You must first provide feedback to the planner.');
- return;
- }
+ const stageObj = JSON.parse(decodeURIComponent(atob(stage)));
- const stageObj = JSON.parse(decodeURIComponent(atob(stage)));
+ console.log("actionStage", {
+ step_id: stageObj.id,
+ plan_id: stageObj.plan_id,
+ session_id: stageObj.session_id,
+ approved: action === "approved" ? true : false,
+ });
- console.log('actionStage', {
- step_id: stageObj.id,
- plan_id: stageObj.plan_id,
- session_id: stageObj.session_id,
- approved: action === 'approved' ? true : false,
- });
+ notyf.open({
+ type: "info",
+ message: `Request sent for "${stageObj.action}"`,
+ });
- notyf.open({
- type: 'info',
- message: `Request sent for "${stageObj.action}"`
+ window.headers.then((headers) => {
+ fetch(apiEndpoint + "/approve_step_or_steps", {
+ method: "POST",
+ headers: headers,
+ body: JSON.stringify({
+ step_id: stageObj.id,
+ plan_id: stageObj.plan_id,
+ session_id: stageObj.session_id,
+ approved: action === "approved" ? true : false,
+ }),
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ console.log("actionStage", data);
+ action === "approved"
+ ? notyf.success(`Stage "${stageObj.action}" approved.`)
+ : notyf.error(`Stage "${stageObj.action}" rejected.`);
+
+ taskDetails();
+ })
+ .catch((error) => {
+ console.error("Error:", error);
});
+ });
+ };
- window.headers
- .then(headers =>{
- fetch(apiEndpoint + '/approve_step_or_steps', {
- method: 'POST',
- headers: headers,
- body: JSON.stringify({
- step_id: stageObj.id,
- plan_id: stageObj.plan_id,
- session_id: stageObj.session_id,
- approved: action === 'approved' ? true : false,
- })
- })
- .then(response => response.json())
- .then(data => {
-
- console.log('actionStage', data);
- action === 'approved' ? notyf.success(`Stage "${stageObj.action}" approved.`) : notyf.error(`Stage "${stageObj.action}" rejected.`);
-
- taskDetails();
-
- })
- .catch(error => {
- console.error('Error:', error);
- })
- })
- }
-
- const actionStages = (task, approve) => {
+ const actionStages = (task, approve) => {
+ console.log("approveStages", {
+ plan_id: task.id,
+ session_id: task.session_id,
+ approved: approve,
+ });
- console.log('approveStages', {
- plan_id: task.id,
- session_id: task.session_id,
- approved: approve,
- });
+ notyf.open({
+ type: "info",
+ message: `Request sent to action on all stages.`,
+ });
- notyf.open({
- type: 'info',
- message: `Request sent to action on all stages.`
+ // document.querySelectorAll('.menu-stage-actions').forEach(element => {
+ // element.style.display = 'none';
+ // });
+
+ window.headers.then((headers) => {
+ fetch(apiEndpoint + "/approve_step_or_steps", {
+ method: "POST",
+ headers: headers,
+ body: JSON.stringify({
+ plan_id: task.id,
+ session_id: task.session_id,
+ approved: approve,
+ }),
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ console.log("approveStages", data);
+ approve
+ ? notyf.success(`All stages approved.`)
+ : notyf.error(`All stages cancelled.`);
+ taskDetails();
+ })
+ .catch((error) => {
+ console.error("Error:", error);
});
-
- // document.querySelectorAll('.menu-stage-actions').forEach(element => {
- // element.style.display = 'none';
- // });
-
- window.headers
- .then(headers =>{
- fetch(apiEndpoint + '/approve_step_or_steps', {
- method: 'POST',
- headers: headers,
- body: JSON.stringify({
- plan_id: task.id,
- session_id: task.session_id,
- approved: approve,
- })
- })
- .then(response => response.json())
- .then(data => {
-
- console.log('approveStages', data);
- approve ? notyf.success(`All stages approved.`) : notyf.error(`All stages rejected.`);
- taskDetails();
-
- })
- .catch(error => {
- console.error('Error:', error);
- })
- })
+ });
+ };
+
+ const taskDetailsActions = () => {
+ if (taskAgentsButton) {
+ taskAgentsButton.addEventListener("click", (event) => {
+ window.parent.postMessage(
+ {
+ button: "taskAgentsButton",
+ id: taskStore.id,
+ name: taskStore.name,
+ },
+ "*"
+ );
+ });
}
- const taskDetailsActions = () => {
-
- if (taskAgentsButton) {
- taskAgentsButton.addEventListener('click', (event) => {
- window.parent.postMessage({
- button: 'taskAgentsButton',
- id: taskStore.id,
- name: taskStore.name
- }, '*');
- });
- }
-
- if (taskWokFlowButton) {
- taskWokFlowButton.addEventListener('click', (event) => {
- window.parent.postMessage({
- button: 'taskWokFlowButton',
- id: taskStore.id,
- name: taskStore.name
- }, '*');
- });
+ if (taskWokFlowButton) {
+ taskWokFlowButton.addEventListener("click", (event) => {
+ window.parent.postMessage(
+ {
+ button: "taskWokFlowButton",
+ id: taskStore.id,
+ name: taskStore.name,
+ },
+ "*"
+ );
+ });
+ }
+ };
+
+ let lastDataHash = null;
+ //Refresh timer
+ const taskDetails = () => {
+ if (taskStore) {
+ taskName.innerHTML = taskStore.name;
+
+ const fetchLoop = async (id) => {
+ try {
+ // Fetch the new data from the server
+ const newData = await fetchPlanDetails(id);
+
+ // Generate a hash of the new data
+ const newDataHash = await GenerateHash(newData);
+
+ // Check if the new data's hash is different from the last fetched data's hash
+ if (newDataHash === lastDataHash) {
+ console.log("Data hasn't changed. Skipping next poll.");
+ return; // Skip polling if no changes
+ }
+
+ // Update the lastDataHash to the new hash
+ lastDataHash = newDataHash;
+
+ // Continue polling by calling fetchLoop again
+ setTimeout(
+ () => fetchLoop(id),
+ Number(sessionStorage.getItem("apiRefreshRate"))
+ );
+ } catch (error) {
+ console.error("Error in fetchLoop:", error);
}
+ };
+ fetchLoop(taskStore.id); // Start the fetch loop
}
+ };
+ const taskMessage = () => {
+ const taskMessageTextarea = document.getElementById("taskMessageTextarea");
- let lastDataHash = null;
- //Refresh timer
- const taskDetails = () => {
- if (taskStore) {
- taskName.innerHTML = taskStore.name;
-
- const fetchLoop = async (id) => {
- try {
- // Fetch the new data from the server
- const newData = await fetchPlanDetails(id);
-
- // Generate a hash of the new data
- const newDataHash = await GenerateHash(newData);
-
- // Check if the new data's hash is different from the last fetched data's hash
- if (newDataHash === lastDataHash) {
- console.log("Data hasn't changed. Skipping next poll.");
- return; // Skip polling if no changes
- }
-
- // Update the lastDataHash to the new hash
- lastDataHash = newDataHash;
-
- // Continue polling by calling fetchLoop again
- setTimeout(() => fetchLoop(id), Number(sessionStorage.getItem('apiRefreshRate')));
- } catch (error) {
- console.error('Error in fetchLoop:', error);
- }
- };
-
- fetchLoop(taskStore.id); // Start the fetch loop
+ if (taskMessageAddButton) {
+ taskMessageAddButton.addEventListener("click", (event) => {
+ const messageContent = taskMessageTextarea.value;
+
+ if (!messageContent) {
+ notyf.error("Please enter a message.");
+ return;
}
- };
+ taskMessageTextarea.disabled = true;
+ taskMessageAddButton.disabled = true;
+ taskMessageAddButton.classList.add("is-loading");
- const taskMessage = () => {
- const taskMessageTextarea = document.getElementById('taskMessageTextarea');
+ console.log({
+ plan_id: taskStore.id,
+ session_id: taskSessionId,
+ human_clarification: taskMessageTextarea.value,
+ });
-
- if (taskMessageAddButton) {
- taskMessageAddButton.addEventListener('click', (event) => {
- const messageContent = taskMessageTextarea.value;
-
- if (!messageContent) {
- notyf.error('Please enter a message.');
- return;
- }
-
- taskMessageTextarea.disabled = true;
- taskMessageAddButton.disabled = true;
- taskMessageAddButton.classList.add('is-loading');
-
- console.log({
- plan_id: taskStore.id,
- session_id: taskSessionId,
- human_clarification: taskMessageTextarea.value,
- })
-
- window.headers
- .then(headers =>{
- fetch(apiEndpoint + '/human_clarification_on_plan', {
- method: 'POST',
- headers: headers,
- body: JSON.stringify({
- plan_id: taskStore.id,
- session_id: taskSessionId,
- human_clarification: taskMessageTextarea.value,
- })
- })
- .then(response => response.json())
- .then(data => {
-
- console.log('taskMessage', data);
-
- taskMessageTextarea.disabled = false;
- taskMessageAddButton.disabled = false;
- taskMessageAddButton.classList.remove('is-loading');
-
- taskMessageTextarea.value = '';
-
- notyf.success('Additional details registered in plan.');
-
- fetchPlanDetails(taskStore.id);
-
- })
- .catch(error => {
- console.error('Error:', error);
- })
- })
+ window.headers.then((headers) => {
+ fetch(apiEndpoint + "/human_clarification_on_plan", {
+ method: "POST",
+ headers: headers,
+ body: JSON.stringify({
+ plan_id: taskStore.id,
+ session_id: taskSessionId,
+ human_clarification: taskMessageTextarea.value,
+ }),
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ console.log("taskMessage", data);
+
+ taskMessageTextarea.disabled = false;
+ taskMessageAddButton.disabled = false;
+ taskMessageAddButton.classList.remove("is-loading");
+
+ taskMessageTextarea.value = "";
+ fetchPlanDetails(taskStore.id);
+
+ // Reset character count to 0
+ const charCount = document.getElementById("charCount");
+ if (charCount) {
+ charCount.textContent = "0";
+ }
+ updateButtonImage();
+
+ notyf.success("Additional details registered in plan.");
+ })
+ .catch((error) => {
+ console.error("Error:", error);
});
- }
-
- }
-
- const handleTextAreaTyping = () => {
- const newTaskPrompt = document.getElementById('taskMessageTextarea');
- newTaskPrompt.addEventListener('input', () => {
- // whenever text is sent to the text area, we want to update the character count and dynamically resize the text area
- const textInput = document.getElementById('taskMessageTextarea');
- const charCount = document.getElementById('charCount');
-
- // Update character count
- charCount.textContent = textInput.value.length;
-
- // Dynamically adjust height
- textInput.style.height = 'auto';
- textInput.style.height = textInput.scrollHeight + 'px';
});
+ });
}
-
- taskHeaderActions();
- taskDetailsActions();
- taskDetails();
- taskMessage();
- handleTextAreaTyping();
-
-})();
\ No newline at end of file
+ };
+
+ const handleTextAreaTyping = () => {
+ const newTaskPrompt = document.getElementById("taskMessageTextarea");
+ newTaskPrompt.addEventListener("input", () => {
+ // whenever text is sent to the text area, we want to update the character count and dynamically resize the text area
+ const textInput = document.getElementById("taskMessageTextarea");
+ const charCount = document.getElementById("charCount");
+
+ // Update character count
+ charCount.textContent = textInput.value.length;
+
+ // Dynamically adjust height
+ textInput.style.height = "auto";
+ textInput.style.height = textInput.scrollHeight + "px";
+ updateButtonImage();
+ });
+ newTaskPrompt.addEventListener("keydown", (event) => {
+ const textValue = newTaskPrompt.value.trim();
+ if (event.key === "Enter" && !event.shiftKey) {
+ if (textValue === "") {
+ event.preventDefault();
+ } else {
+ startTaskButton.click();
+ }
+ } else if (event.key === "Enter" && event.shiftKey) {
+ return;
+ }
+ });
+ };
+ updateButtonImage();
+ taskHeaderActions();
+ taskDetailsActions();
+ taskDetails();
+ taskMessage();
+ handleTextAreaTyping();
+})();