personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(agents): add restart button for completed agents

Adds a restart icon button to each row in the historical completed agents
table that allows users to re-run an agent with the same configuration.
The button shows a confirmation dialog before creating a new agent run
with a fresh timestamp and ID.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

+120 -16
+97 -16
dream/templates/agents/_scripts.html
··· 736 736 let totalAgents = 0; 737 737 738 738 // Session history table functions 739 - function buildRow(a) { 739 + function buildRow(a, isHistorical = false) { 740 740 const tr = document.createElement('tr'); 741 741 const short = a.prompt.length > 80 ? a.prompt.slice(0, 80) + '…' : a.prompt; 742 742 const chatLink = `{{ url_for('chat.chat_page') }}?agent=${a.id}`; 743 - 743 + 744 744 // Status badge 745 745 const statusClass = `status-${a.status || 'unknown'}`; 746 746 const statusBadge = `<span class="status-badge ${statusClass}">${a.status || 'unknown'}</span>`; 747 - 747 + 748 748 // PID indicator for running agents 749 749 const pidIndicator = a.pid ? ` (PID: ${a.pid})` : ''; 750 - 750 + 751 751 // Format runtime 752 752 let runtimeDisplay = ''; 753 753 if (a.runtime_seconds !== undefined && a.runtime_seconds !== null) { 754 754 const totalSeconds = Math.floor(a.runtime_seconds); 755 755 const minutes = Math.floor(totalSeconds / 60); 756 - 756 + 757 757 if (totalSeconds > 59) { 758 758 // For 60 seconds or more, show just minutes 759 759 runtimeDisplay = `${minutes}m`; ··· 762 762 runtimeDisplay = `${totalSeconds}s`; 763 763 } 764 764 } 765 - 765 + 766 766 tr.innerHTML = ` 767 767 <td><a href="${chatLink}">${statusBadge}</a></td> 768 768 <td><a href="${chatLink}">${a.since}</a></td> ··· 771 771 <td>${a.persona_title || a.persona}</td> 772 772 <td title="${a.prompt}"><a href="${chatLink}">${short}</a></td> 773 773 `; 774 + 775 + // Add restart button cell for historical agents 776 + if (isHistorical) { 777 + const restartCell = document.createElement('td'); 778 + restartCell.style.textAlign = 'center'; 779 + restartCell.style.padding = '0'; 780 + 781 + const restartBtn = document.createElement('button'); 782 + restartBtn.className = 'restart-btn'; 783 + restartBtn.title = 'Restart this agent'; 784 + restartBtn.innerHTML = '&#x21bb;'; 785 + restartBtn.dataset.agentId = a.id; 786 + restartBtn.dataset.prompt = a.prompt; 787 + restartBtn.dataset.persona = a.persona; 788 + restartBtn.dataset.backend = a.model; 789 + 790 + restartBtn.onclick = (e) => { 791 + e.stopPropagation(); 792 + restartAgent(e.target); 793 + }; 794 + 795 + restartCell.appendChild(restartBtn); 796 + tr.appendChild(restartCell); 797 + } 798 + 774 799 tr.style.cursor = 'pointer'; 775 - tr.onclick = () => window.location.href = chatLink; 800 + tr.onclick = (e) => { 801 + // Don't navigate if clicking the restart button 802 + if (e.target.classList.contains('restart-btn')) { 803 + return; 804 + } 805 + window.location.href = chatLink; 806 + }; 776 807 return tr; 777 808 } 778 809 ··· 807 838 // Update live agents table 808 839 const liveTable = document.getElementById('liveAgentTable'); 809 840 const liveEmpty = document.getElementById('liveEmpty'); 810 - 841 + 811 842 if (liveAgents.length > 0) { 812 843 liveTable.innerHTML = '<tr><th>Status</th><th>Started</th><th>Runtime</th><th>Model</th><th>Agent</th><th>Prompt</th></tr>'; 813 - liveAgents.forEach(a => liveTable.appendChild(buildRow(a))); 844 + liveAgents.forEach(a => liveTable.appendChild(buildRow(a, false))); 814 845 liveTable.style.display = 'table'; 815 846 liveEmpty.style.display = 'none'; 816 847 } else { 817 848 liveTable.style.display = 'none'; 818 849 liveEmpty.style.display = 'block'; 819 850 } 820 - 851 + 821 852 // Update historical agents table 822 853 const histTable = document.getElementById('historicalAgentTable'); 823 854 const histEmpty = document.getElementById('historicalEmpty'); 824 - 855 + 825 856 if (historicalAgents.length > 0) { 826 - histTable.innerHTML = '<tr><th>Status</th><th>Started</th><th>Runtime</th><th>Model</th><th>Agent</th><th>Prompt</th></tr>'; 827 - historicalAgents.forEach(a => histTable.appendChild(buildRow(a))); 857 + histTable.innerHTML = '<tr><th>Status</th><th>Started</th><th>Runtime</th><th>Model</th><th>Agent</th><th>Prompt</th><th style="width: 40px;">Actions</th></tr>'; 858 + historicalAgents.forEach(a => histTable.appendChild(buildRow(a, true))); 828 859 histTable.style.display = 'table'; 829 860 histEmpty.style.display = 'none'; 830 861 } else { ··· 865 896 // Update historical agents table 866 897 const histTable = document.getElementById('historicalAgentTable'); 867 898 const histEmpty = document.getElementById('historicalEmpty'); 868 - 899 + 869 900 if (historicalAgents.length > 0) { 870 - histTable.innerHTML = '<tr><th>Status</th><th>Started</th><th>Runtime</th><th>Model</th><th>Agent</th><th>Prompt</th></tr>'; 871 - historicalAgents.forEach(a => histTable.appendChild(buildRow(a))); 901 + histTable.innerHTML = '<tr><th>Status</th><th>Started</th><th>Runtime</th><th>Model</th><th>Agent</th><th>Prompt</th><th style="width: 40px;">Actions</th></tr>'; 902 + historicalAgents.forEach(a => histTable.appendChild(buildRow(a, true))); 872 903 histTable.style.display = 'table'; 873 904 histEmpty.style.display = 'none'; 874 905 } else { ··· 949 980 // Show error message 950 981 function showError(message) { 951 982 alert(message); // Simple alert for now, can be improved with a toast notification 983 + } 984 + 985 + // Restart agent function 986 + async function restartAgent(buttonElement) { 987 + // Get data from button's dataset 988 + const agentId = buttonElement.dataset.agentId; 989 + const prompt = buttonElement.dataset.prompt; 990 + const persona = buttonElement.dataset.persona; 991 + const backend = buttonElement.dataset.backend; 992 + 993 + // Confirmation dialog 994 + const confirmed = confirm( 995 + `Are you sure you want to restart this agent?\n\n` + 996 + `Prompt: ${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}\n` + 997 + `Agent: ${persona}\n` + 998 + `Backend: ${backend}\n\n` + 999 + `This will create a new agent run with the same configuration.` 1000 + ); 1001 + 1002 + if (!confirmed) { 1003 + return; 1004 + } 1005 + 1006 + try { 1007 + const response = await fetch('{{ url_for('agents.start_agent') }}', { 1008 + method: 'POST', 1009 + headers: { 1010 + 'Content-Type': 'application/json', 1011 + }, 1012 + body: JSON.stringify({ 1013 + prompt: prompt, 1014 + backend: backend, 1015 + persona: persona, 1016 + config: {} 1017 + }) 1018 + }); 1019 + 1020 + const data = await response.json(); 1021 + 1022 + if (!response.ok) { 1023 + throw new Error(data.error || 'Failed to restart agent'); 1024 + } 1025 + 1026 + // Success - reload the agents list to show the new agent 1027 + alert('Agent restarted successfully!'); 1028 + loadAgentSessions(currentPage); 1029 + 1030 + } catch (error) { 1031 + showError('Error restarting agent: ' + error.message); 1032 + } 952 1033 } 953 1034 954 1035 // Create Agent Modal Functions
+23
dream/templates/agents/_styles.html
··· 1095 1095 .tool-params.collapsed .tool-params-toggle::before { 1096 1096 transform: rotate(-90deg); 1097 1097 } 1098 + 1099 + /* Restart button styling */ 1100 + .restart-btn { 1101 + background: none; 1102 + border: none; 1103 + font-size: 1.2rem; 1104 + cursor: pointer; 1105 + padding: 4px 8px; 1106 + color: #007bff; 1107 + transition: all 0.2s ease; 1108 + line-height: 1; 1109 + } 1110 + 1111 + .restart-btn:hover { 1112 + color: #0056b3; 1113 + transform: scale(1.2); 1114 + background: #f8f9ff; 1115 + border-radius: 4px; 1116 + } 1117 + 1118 + .restart-btn:active { 1119 + transform: scale(1.0); 1120 + }