Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c07bbba
InlineButtonConfirmations
Dec 21, 2025
6d31f86
css & html cleanup
Dec 21, 2025
b823fcf
refactor settings and scheduler
3clyp50 Dec 11, 2025
7bd3487
move a2a out of mcp dir (settings)
3clyp50 Dec 22, 2025
4fecec0
fix: scheduler polling unchanged task list
3clyp50 Dec 22, 2025
43cc250
conform settings modal footer
3clyp50 Dec 22, 2025
745d4eb
fix scheduler flickering
3clyp50 Dec 22, 2025
d3a0248
task detail ux/quality of life
3clyp50 Dec 22, 2025
06a6632
Merge remote-tracking branch 'upstream/settings-refactor' into schedu…
3clyp50 Dec 22, 2025
987bb1b
Update buttons.css
frdel Dec 22, 2025
9956fca
fix css
keyboardstaff Dec 22, 2025
1afe154
Merge branch 'settings-refactor' into schedulercmp
3clyp50 Dec 22, 2025
78e735f
fix css
keyboardstaff Dec 23, 2025
fbc729d
fix css
keyboardstaff Dec 23, 2025
78e6f74
refactor settings frontend for new data structure
3clyp50 Dec 26, 2025
1916958
Update FUNDING.yml
frdel Dec 26, 2025
601bb56
Merge branch 'main' into development
frdel Dec 26, 2025
a6ea619
standalone scheduler modal
3clyp50 Dec 26, 2025
76fd1b7
scheduler redesign
3clyp50 Dec 26, 2025
0d92bb7
shared css classes for memory-dash and scheduler
3clyp50 Dec 26, 2025
3722edc
fix: edit task with project
3clyp50 Dec 26, 2025
cc7af97
Fix prompt fence stripping policy
Dec 28, 2025
a54768c
showcase video redacted
frdel Dec 29, 2025
ea4968a
Merge branch 'main' into development
frdel Dec 29, 2025
7d1df7e
scheduler cleanup
3clyp50 Dec 29, 2025
9fe1080
Add inline button confirmations for Reset Chat and Restart
keyboardstaff Dec 29, 2025
d4587e0
ui: streamline sidebar buttons, add dropdown component
3clyp50 Dec 29, 2025
fd1fc8e
Merge branch 'pr/849' into development
frdel Dec 29, 2025
3281427
Merge branch 'pr/860' into development
frdel Dec 30, 2025
98fe3e8
fix for scheduler and memory dashboard style
3clyp50 Jan 2, 2026
4d2830b
disable save chat btn if no chat selected
3clyp50 Jan 2, 2026
244913c
disabled dropdown btn style
3clyp50 Jan 2, 2026
ed0964a
sensitive settings store/load, short guids in scheduler and vdb
frdel Jan 2, 2026
71b2571
Merge branch 'schedulercmp' of https://github.com/3clyp50/agent-zero …
frdel Jan 2, 2026
8c3daa6
unify scrollbars and fix tunnel spinner
3clyp50 Jan 2, 2026
c3c8d12
fix editor disappearing in mcp/a2a examples
3clyp50 Jan 2, 2026
b53ab41
dropdown scrollbar
3clyp50 Jan 2, 2026
f1d12c7
display mcp token in json editor
3clyp50 Jan 2, 2026
67e5783
tooltips; css polishing
3clyp50 Jan 2, 2026
400095c
refactor settings and scheduler
3clyp50 Dec 11, 2025
06e6d0d
memory dashboard cleanup
3clyp50 Jan 2, 2026
3d8fc37
tooltip hideAll coverage in chat btns
3clyp50 Jan 2, 2026
ae812f6
disable resetChat on dashboard
3clyp50 Jan 4, 2026
3e07fd0
projects selector dropdown polish
3clyp50 Jan 4, 2026
6d4c6bb
light mode colors rework
3clyp50 Jan 4, 2026
67256cc
fix hidden/hanging tooltips
3clyp50 Jan 4, 2026
48627e4
bs tooltips fix; consistent icons
3clyp50 Jan 6, 2026
2d8054e
confirmClick for dropdown + btm actions resetChat
3clyp50 Jan 6, 2026
1f0bbc2
task sync from sidebar in scheduler store
3clyp50 Jan 6, 2026
04cd6f6
extend confirmClick coverage
3clyp50 Jan 6, 2026
0b9235a
fix: dropdown behind sidebar-bottom
3clyp50 Jan 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from datetime import datetime, timezone
from typing import Any, Awaitable, Coroutine, Dict, Literal
from enum import Enum
import uuid
import models

from python.helpers import (
Expand Down
1,232 changes: 53 additions & 1,179 deletions python/helpers/settings.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions python/helpers/task_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from python.helpers.defer import DeferredTask
from python.helpers.files import get_abs_path, make_dirs, read_file, write_file
from python.helpers.localization import Localization
from python.helpers import projects
from python.helpers import projects, guids
import pytz
from typing import Annotated

Expand Down Expand Up @@ -118,7 +118,7 @@ def should_launch(self) -> datetime | None:


class BaseTask(BaseModel):
uuid: str = Field(default_factory=lambda: str(uuid.uuid4()))
uuid: str = Field(default_factory=lambda: guids.generate_id())
context_id: Optional[str] = Field(default=None)
state: TaskState = Field(default=TaskState.IDLE)
name: str = Field()
Expand Down
4 changes: 2 additions & 2 deletions python/helpers/vector_db.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from typing import Any, List, Sequence
import uuid
from langchain_community.vectorstores import FAISS

# faiss needs to be patched for python 3.12 on arm #TODO remove once not needed
Expand All @@ -17,6 +16,7 @@
from simpleeval import simple_eval

from agent import Agent
from python.helpers import guids


class MyFaiss(FAISS):
Expand Down Expand Up @@ -99,7 +99,7 @@ async def search_by_metadata(self, filter: str, limit: int = 0) -> list[Document
return result

async def insert_documents(self, docs: list[Document]):
ids = [str(uuid.uuid4()) for _ in range(len(docs))]
ids = [guids.generate_id() for _ in range(len(docs))]

if ids:
for doc, id in zip(docs, ids):
Expand Down
19 changes: 16 additions & 3 deletions webui/components/chat/input/bottom-actions.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { store } from "/components/chat/input/input-store.js";
import { store as historyStore } from "/components/modals/history/history-store.js";
import { store as contextStore } from "/components/modals/context/context-store.js";
import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
</script>
</head>
<body>
Expand All @@ -24,6 +25,16 @@
<span x-text="$store.chatInput.paused ? 'Resume Agent' : 'Pause Agent'"></span>
</button>

<button class="text-button" @click="$confirmClick($event, () => $store.chats.resetChat())" :disabled="!$store.chats.selected">
<span class="material-symbols-outlined">delete_sweep</span>
<p>Clear Chat</p>
</button>

<button class="text-button" @click="$store.chats.saveChat()" :disabled="!$store.chats.selected">
<span class="material-symbols-outlined">save</span>
<p>Save Chat</p>
</button>

<button class="text-button" @click="$store.chatInput.loadKnowledge()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"></path>
Expand Down Expand Up @@ -69,12 +80,14 @@
<style>
.text-buttons-row { width: 100%; display: flex; padding-top: var(--spacing-xs); margin-left: var(--spacing-xs); }
.text-button { background-color: transparent; border: none; border-radius: 5px; color: var(--color-text); font-family: "Rubik", Arial, Helvetica, sans-serif; font-size: 0.6rem; padding: 6px var(--spacing-sm); cursor: pointer; opacity: 0.8; -webkit-transition: all 0.3s; transition: all 0.3s; display: flex; align-items: center; gap: var(--spacing-xs); }
.text-button:hover { opacity: 1; background-color: var(--color-secondary); border-radius: 4px; }
.text-button:active { opacity: 0.5; }
.text-button:hover:not(:disabled) { opacity: 1; background-color: var(--color-background-hover); border-radius: 4px; }
.text-button:active:not(:disabled) { opacity: 0.5; }
.text-button:disabled { opacity: 0.4; cursor: not-allowed; }
.text-button svg { width: 14px; height: 14px; flex-shrink: 0; }
.text-button .material-symbols-outlined { font-size: 14px; flex-shrink: 0; }
.text-button p { margin-block: 0; }

@media (max-width: 768px) { .text-buttons-row { width: 90%; display: flex; padding-top: var(--spacing-xs); gap: var(--spacing-xs); white-space: pre-wrap; } .text-button { font-size: 0.6rem; } .text-button svg { width: 18px; height: 18px; flex-shrink: 0; } }
@media (max-width: 768px) { .text-buttons-row { width: 90%; display: flex; padding-top: var(--spacing-xs); gap: var(--spacing-xs); white-space: pre-wrap; } .text-button { font-size: 0.6rem; } .text-button svg { width: 18px; height: 18px; flex-shrink: 0; } .text-button .material-symbols-outlined { font-size: 18px; } }
@media (max-width: 640px) { .text-buttons-row { display: flex; width: 90%; padding-top: var(--spacing-xs); gap: var(--spacing-xs); white-space: pre-wrap; } .text-button { max-height: 25px; } .text-button p { display: none; } }
</style>
</body>
Expand Down
11 changes: 2 additions & 9 deletions webui/components/chat/input/chat-bar-input.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@
<body>
<div class="input-row">
<!-- Attachment icon with tooltip -->
<div class="attachment-wrapper" x-data="{ showTooltip: false }">
<label for="file-input" class="attachment-icon" @mouseover="showTooltip = true" @mouseleave="showTooltip = false">
<div class="attachment-wrapper">
<label for="file-input" class="attachment-icon" title="Add attachments to the message" data-bs-placement="top" data-bs-trigger="hover">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z" />
</svg>
</label>
<input type="file" id="file-input" accept="*" multiple style="display: none" @change="$store.chatAttachments.handleFileUpload($event)">

<div x-show="showTooltip" class="tooltip">Add attachments to the message</div>
</div>

<!-- Container for textarea and expand button -->
Expand Down Expand Up @@ -64,8 +62,6 @@
.attachment-icon { cursor: pointer; color: var(--color-text); opacity: 0.7; transition: opacity 0.2s ease; display: flex; align-items: center; }
.attachment-icon:hover { opacity: 1; }
.attachment-icon:active { opacity: 0.5; }
.tooltip { position: absolute; bottom: 100%; left: 50%; transform: translateX(0%); padding: 8px; background-color: var(--color-secondary); color: var(--color-text); border-radius: 4px; font-size: 12px; white-space: nowrap; z-index: 1002; }

/* Text input */
#chat-input-container {
position: relative;
Expand Down Expand Up @@ -117,8 +113,6 @@
.chat-button svg { width: 1.5rem; height: 1.5rem; }
/* Microphone button */
.chat-button.mic-inactive svg { /* Add specific styles if needed */ }
/* Tooltip */
.tooltip { position: absolute; bottom: 100%; left: 50%; transform: translateX(0%); padding: 8px; background-color: var(--color-secondary); color: var(--color-text); border-radius: 4px; font-size: 12px; white-space: nowrap; z-index: 1002; }
/* Responsive tweaks */
@media (max-width: 640px) {
#chat-input { min-height: 5.3rem; align-content: start; }
Expand All @@ -128,4 +122,3 @@
</style>
</body>
</html>

22 changes: 12 additions & 10 deletions webui/components/chat/speech/speech-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,18 @@ const model = {
try {
const response = await fetchApi("/settings_get", { method: "POST" });
const data = await response.json();
const speechSection = data.settings.sections.find(
(s) => s.title === "Speech"
);

if (speechSection) {
speechSection.fields.forEach((field) => {
if (this.hasOwnProperty(field.id)) {
this[field.id] = field.value;
}
});
const settings = data?.settings || {};

if (settings) {
this.stt_model_size = settings.stt_model_size ?? this.stt_model_size;
this.stt_language = settings.stt_language ?? this.stt_language;
this.stt_silence_threshold =
settings.stt_silence_threshold ?? this.stt_silence_threshold;
this.stt_silence_duration =
settings.stt_silence_duration ?? this.stt_silence_duration;
this.stt_waiting_timeout =
settings.stt_waiting_timeout ?? this.stt_waiting_timeout;
this.tts_kokoro = settings.tts_kokoro ?? this.tts_kokoro;
}
} catch (error) {
window.toastFetchError("Failed to load speech settings", error);
Expand Down
69 changes: 69 additions & 0 deletions webui/components/dropdown/dropdown.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!--
Reusable Dropdown Component

Usage:
<x-component data-component="ui/dropdown" data-props='{"id": "my-dropdown"}'>
<template x-slot:trigger>
<button>Click me</button>
</template>
<template x-slot:menu>
<button class="dropdown-item" @click="...">Item 1</button>
<div class="dropdown-separator"></div>
<button class="dropdown-item" @click="...">Item 2</button>
</template>
</x-component>

Or use the simpler inline approach with x-data:
<div class="dropdown" x-data="{ open: false }" @click.outside="open = false">
<button class="dropdown-trigger" @click="open = !open">...</button>
<div class="dropdown-menu" x-show="open" x-transition>...</div>
</div>
-->
<html>
<head>
<!-- Dropdown uses global styles from index.css -->
</head>
<body>
<div
class="dropdown"
x-data="{
open: false,
toggle() { this.open = !this.open; },
close() { this.open = false; }
}"
@click.outside="close()"
@keydown.escape.window="close()"
>
<!-- Trigger slot -->
<div class="dropdown-trigger-wrapper" @click="toggle()">
<slot name="trigger">
<button class="dropdown-trigger" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 9l6 6 6-6"/>
</svg>
</button>
</slot>
</div>

<!-- Menu slot -->
<div
class="dropdown-menu"
x-show="open"
@click="close()"
>
<slot name="menu">
<!-- Default empty menu -->
</slot>
</div>
</div>

<style>
/* Component-specific styles (layout helpers) */
.dropdown-trigger-wrapper {
display: contents;
}

</style>
</body>
</html>

5 changes: 2 additions & 3 deletions webui/components/modals/file-browser/file-browser-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ const model = {

// --- File actions --------------------------------------------------------
async deleteFile(file) {
if (!confirm(`Are you sure you want to delete ${file.name}?`)) return;
try {
const resp = await fetchApi("/delete_work_dir_file", {
method: "POST",
Expand All @@ -186,9 +185,9 @@ const model = {
this.browser.entries = this.browser.entries.filter(
(e) => e.path !== file.path
);
alert("File deleted successfully.");
window.toastFrontendSuccess("File deleted successfully", "File Deleted");
} else {
alert(`Error deleting file: ${await resp.text()}`);
window.toastFrontendError(`Error deleting file: ${await resp.text()}`, "Delete Error");
}
} catch (e) {
window.toastFrontendError(
Expand Down
43 changes: 4 additions & 39 deletions webui/components/modals/file-browser/file-browser.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@
<div class="file-size" x-text="$store.fileBrowser.formatFileSize(file.size)"></div>
<div class="file-date" x-text="$store.fileBrowser.formatDate(file.modified)"></div>
<div class="file-actions">
<button class="action-button download-button" @click.stop="$store.fileBrowser.downloadFile(file)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.5 19.5"><path d="m.75,14.25v2.25c0,1.24,1.01,2.25,2.25,2.25h13.5c1.24,0,2.25-1.01,2.25-2.25v-2.25m-4.5-4.5l-4.5,4.5m0,0l-4.5-4.5m4.5,4.5V.75" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/></svg>
<button class="btn-icon-action" @click.stop="$store.fileBrowser.downloadFile(file)" title="Download file">
<span class="material-symbols-outlined">download</span>
</button>
<button class="delete-button" @click.stop="$store.fileBrowser.deleteFile(file)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.03 22.53" fill="currentColor"><path d="m14.55,7.82H4.68L14.09,3.19c.83-.41,1.17-1.42.77-2.25-.41-.83-1.42-1.17-2.25-.77l-3.16,1.55-.15-.31c-.22-.44-.59-.76-1.05-.92-.46-.16-.96-.13-1.39.09l-2.08,1.02c-.9.44-1.28,1.54-.83,2.44l.15.31-3.16,1.55c-.83.41-1.17,1.42-.77,2.25.29.59.89.94,1.51.94.25,0,.5-.06.74-.17l.38-.19s.09.03.14.03h11.14v11.43c0,.76-.62,1.38-1.38,1.38h-.46v-11.28c0-.26-.21-.47-.47-.47s-.47.21-.47.47v11.28h-2.39v-11.28c0-.26-.21-.47-.47-.47s-.47.21-.47.47v11.28h-2.39v-11.28c0-.26-.21-.47-.47-.47s-.47.21-.47.47v11.28h-.46c-.76,0-1.38-.62-1.38-1.38v-9.9c0-.26-.21-.47-.47-.47s-.47.21-.47.47v9.9c0,1.28,1.04,2.32,2.32,2.32h8.55c1.28,0,2.32-1.04,2.32-2.32v-11.91c0-.26-.21-.47-.47-.47Z" stroke-width="0"/></svg>
<button class="btn-icon-action" @click.stop="$confirmClick($event, () => $store.fileBrowser.deleteFile(file))" title="Delete file">
<span class="material-symbols-outlined">delete</span>
</button>
</div>
</div>
Expand Down Expand Up @@ -279,47 +279,12 @@
.btn-upload:active {
background-color: #2b309c;
}
/* Delete Button Styles */
.delete-button {
background: none;
border: none;
color: var(--color-primary);
cursor: pointer;
width: 32px;
padding: 4px 8px;
border-radius: 4px;
transition: opacity 0.2s, background-color 0.2s;
}
.delete-button:hover {
color: #ff7878;
}
.delete-button:active {
opacity: 0.6;
}

/* File Actions */
.file-actions {
display: flex;
gap: var(--spacing-xs);
}
.action-button {
background: none;
border: none;
cursor: pointer;
width: 32px;
padding: 6px 8px;
border-radius: 4px;
transition: background-color 0.2s;
}
.download-button {
color: var(--color-primary);
}
.download-button:hover {
background-color: var(--color-border);
}
.light-mode .download-button:hover {
background-color: #c6d4de;
}
/* Responsive Design */
@media (max-width: 768px) {
.file-header,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</button>
</div>
<div class="toolbar-group">
<button class="toolbar-button" @click="$store.fullScreenInputModal.clearText()" title="Clear Text">
<button class="toolbar-button" @click="$confirmClick($event, () => $store.fullScreenInputModal.clearText())" title="Clear Text">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
Expand Down
22 changes: 22 additions & 0 deletions webui/components/modals/image-viewer/image-viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@
position: relative;
}

/* Unified scrollbar styling for modal content */
.image-display-wrapper::-webkit-scrollbar {
width: 6px;
height: 6px;
}

.image-display-wrapper::-webkit-scrollbar-track {
background: transparent;
margin: 4px 0;
border-radius: 6px;
}

.image-display-wrapper::-webkit-scrollbar-thumb {
background-color: rgba(155, 155, 155, 0.5);
border-radius: 6px;
transition: background-color 0.2s ease;
}

.image-display-wrapper::-webkit-scrollbar-thumb:hover {
background-color: rgba(155, 155, 155, 0.7);
}

.modal-image {
max-width: 100%;
max-height: 100%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const memoryDashboardStore = {
pollingEnabled: false,

async openModal() {
await openModal("settings/memory/memory-dashboard.html");
await openModal("modals/memory/memory-dashboard.html");
},

init() {
Expand Down Expand Up @@ -324,9 +324,6 @@ const memoryDashboardStore = {
const selectedMemories = this.selectedMemories;
if (selectedMemories.length === 0) return;

const confirmMessage = `Are you sure you want to delete ${selectedMemories.length} selected memories? This cannot be undone.`;
if (!confirm(confirmMessage)) return;

try {
this.loading = true;
const response = await API.callJsonApi("memory_dashboard", {
Expand Down Expand Up @@ -451,7 +448,7 @@ ${memory.content_full}
this.editMode = false;
this.editMemoryBackup = null;
// Use global modal system
openModal("settings/memory/memory-detail-modal.html");
openModal("modals/memory/memory-detail-modal.html");
},

closeMemoryDetails() {
Expand Down Expand Up @@ -554,14 +551,6 @@ ${memory.content_full}
},

async deleteMemory(memory) {
if (
!confirm(
`Are you sure you want to delete this memory from ${memory.area}?`
)
) {
return;
}

try {
// Check if this is the memory currently being viewed in detail modal
const isViewingThisMemory =
Expand Down
Loading