feat: enhance daemon dispatching, device readiness fallback, and live streaming UI

- Admin Console & Task Queue: add cancellation, session startup progress endpoints, and support ingress metadata
- Diagnostics & Runtime: enhance ADB probe to prefer unlocked devices and synchronize with readiness engine
- MCP Server: dispatch tasks through Artemis Daemon scheduler with fallback to standalone runner, improve action overlays
- Showcase UI: improve session auto-selection with pinning support, render startup progress, and refine stream styling
- CLI & Makefile: expose server lifecycle commands (restart, stop, status)
- Configuration: ignore runtime server state file in .gitignore
This commit is contained in:
somew1nd committed 2026-08-26 17:41:23 -07:00
1 parent 57655266cc
commit 8fdecf23d7
33 files changed
+1315 -293

No files matched your search

@@ -74,6 +74,16 @@
</span>
<span class="item-time-divider" *ngIf="getDeviceSerial(session)">·</span>
<span class="item-time">{{ session.start_time * 1000 | date:'shortTime' }}</span>
<button
*ngIf="getTaskStatus(session) === 'running' || getTaskStatus(session) === 'paused'"
type="button"
class="btn-stop-card"
(click)="stopTask(session.session_id, $event)"
title="Stop this task"
aria-label="Stop this task"
>
<span class="material-symbols-outlined">stop_circle</span>
</button>
<button
*ngIf="getTaskStatus(session) !== 'running' && getTaskStatus(session) !== 'paused'"
type="button"
@@ -551,11 +551,14 @@ $stream-row-height: 25px;
.item-header .item-header-actions {
&:has(.btn-delete-card) .item-time,
&:has(.btn-delete-card) .item-time-divider {
&:has(.btn-delete-card) .item-time-divider,
&:has(.btn-stop-card) .item-time,
&:has(.btn-stop-card) .item-time-divider {
opacity: 0;
}
.btn-delete-card {
.btn-delete-card,
.btn-stop-card {
opacity: 1;
pointer-events: auto;
}
@@ -756,6 +759,38 @@ $stream-row-height: 25px;
background: #fee2e2;
}
}
.btn-stop-card {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 18px;
height: 18px;
padding: 0;
border: none;
background: transparent;
border-radius: 3px;
color: #ef4444;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
transition: all 0.15s ease;
.material-symbols-outlined {
font-size: 14px;
line-height: 1;
}
&:hover {
color: #dc2626;
background: #fee2e2;
transform: translateY(-50%) scale(1.1);
}
}
}
}
@@ -643,6 +643,11 @@ export class AgentStreamComponent implements AfterViewInit {
this.isTaskDropdownOpen.set(false);
}
public stopTask(sessionId: string, event?: Event): void {
if (event) event.stopPropagation();
this.agentService.stopTask(sessionId, false);
}
public deleteTask(sessionId: string, event?: Event): void {
if (event) event.stopPropagation();
if (!confirm(`Are you sure you want to delete this task? This cannot be undone.`)) {
@@ -83,6 +83,16 @@
</span>
<span class="task-time-divider" *ngIf="getDeviceSerial(session)">·</span>
<span class="task-time">{{ session.start_time * 1000 | date:'shortTime' }}</span>
<button
*ngIf="getTaskStatus(session) === 'running' || getTaskStatus(session) === 'paused'"
type="button"
class="btn-stop-card"
(click)="stopTask(session.session_id, $event)"
title="Stop this task"
aria-label="Stop this task"
>
<span class="material-symbols-outlined">stop_circle</span>
</button>
<button
*ngIf="getTaskStatus(session) !== 'running' && getTaskStatus(session) !== 'paused'"
type="button"
@@ -297,11 +297,14 @@ $text-muted: #a1a1aa;
.task-card-header .task-card-header-actions {
&:has(.btn-delete-card) .task-time,
&:has(.btn-delete-card) .task-time-divider {
&:has(.btn-delete-card) .task-time-divider,
&:has(.btn-stop-card) .task-time,
&:has(.btn-stop-card) .task-time-divider {
opacity: 0;
}
.btn-delete-card {
.btn-delete-card,
.btn-stop-card {
opacity: 1;
pointer-events: auto;
}
@@ -508,6 +511,43 @@ $text-muted: #a1a1aa;
cursor: not-allowed;
}
}
.btn-stop-card {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
padding: 0;
border: none;
background: transparent;
border-radius: 4px;
color: #ef4444;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
transition: all 0.15s ease;
.material-symbols-outlined {
font-size: 15px;
line-height: 1;
}
&:hover:not(:disabled) {
color: #dc2626;
background: #fee2e2;
transform: translateY(-50%) scale(1.1);
}
&:disabled {
opacity: 0.3;
cursor: not-allowed;
}
}
}
}
@@ -98,6 +98,19 @@ export class ChatInterfaceComponent {
});
}
/**
* Stop an individual task session
*/
public stopTask(sessionId: string, event: MouseEvent): void {
event.stopPropagation();
this.isSubmitting = true;
this.errorMessage = null;
this.agentService.stopTask(sessionId, false);
setTimeout(() => {
this.isSubmitting = false;
}, 400);
}
/**
* Stop the currently running task and optionally clear the backend queue
*/