summaryrefslogtreecommitdiffhomepage
path: root/packages/frontend/src/lib/components/TaskListPanel.svelte
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-22 00:19:14 +0900
committerAdam Malczewski <[email protected]>2026-05-22 00:19:14 +0900
commitfb97d4cb72d0a90dde102b7001603716ee6e4c3b (patch)
tree55c6e8b56b3395008523ab94c16c4f526083d846 /packages/frontend/src/lib/components/TaskListPanel.svelte
parent7884709e3b2adb1b65c1c086257e0300eed51cee (diff)
downloaddispatch-fb97d4cb72d0a90dde102b7001603716ee6e4c3b.tar.gz
dispatch-fb97d4cb72d0a90dde102b7001603716ee6e4c3b.zip
feat: agent summoning system, todo improvements, security fixes, double-execution bug fix
- Add summon/retrieve tools for spawning child agents in new tabs - summon: non-blocking, returns agent_id immediately - retrieve: blocking, waits for child to finish, returns result - Child tools are intersected with parent permissions (no privilege escalation) - Working directory validated to stay within workspace - Abort controller stops orphaned agents on tab close - Rename task_list tool to todo with comprehensive usage guidance in system prompt - Rename PermissionLog.svelte to ToolPermissions.svelte - Add 'Summon agents' toggle to tool permissions UI - Redesign TaskListPanel with DaisyUI checkboxes (indeterminate for in-progress) - Remove 'blocked' status from task system - Add tab-created WebSocket event for child agent tab visibility - Add HMR cleanup for WebSocket connections (close stale connections on hot reload) - Fix ensureAssistantMessage to not throw on closed tabs - Fix double tool execution: remove execute from AI SDK tool() in registry.ts (agent.ts already executes tools manually via executeToolWithStreaming) - Fix all pre-existing test failures (missing mocks, stale API signatures) - Add debug info to copy button (tab ID, injected skills, all tab IDs) - Add tab ID and tools to conversation copy output
Diffstat (limited to 'packages/frontend/src/lib/components/TaskListPanel.svelte')
-rw-r--r--packages/frontend/src/lib/components/TaskListPanel.svelte103
1 files changed, 57 insertions, 46 deletions
diff --git a/packages/frontend/src/lib/components/TaskListPanel.svelte b/packages/frontend/src/lib/components/TaskListPanel.svelte
index 5f2ffe2..d70373f 100644
--- a/packages/frontend/src/lib/components/TaskListPanel.svelte
+++ b/packages/frontend/src/lib/components/TaskListPanel.svelte
@@ -1,41 +1,45 @@
<script lang="ts">
- interface TaskItem {
- id: string;
- title: string;
- description: string;
- status: "pending" | "in_progress" | "done" | "blocked";
- }
+interface TaskItem {
+ id: string;
+ title: string;
+ description: string;
+ status: "pending" | "in_progress" | "done";
+}
- const { tasks }: { tasks: TaskItem[] } = $props();
+const { tasks }: { tasks: TaskItem[] } = $props();
- const doneCount = $derived(tasks.filter((t) => t.status === "done").length);
- const inProgressCount = $derived(tasks.filter((t) => t.status === "in_progress").length);
+const doneCount = $derived(tasks.filter((t) => t.status === "done").length);
+const inProgressCount = $derived(tasks.filter((t) => t.status === "in_progress").length);
- function badgeClass(status: TaskItem["status"]): string {
- switch (status) {
- case "pending":
- return "badge badge-ghost badge-xs";
- case "in_progress":
- return "badge badge-info badge-xs";
- case "done":
- return "badge badge-success badge-xs";
- case "blocked":
- return "badge badge-warning badge-xs";
- }
+function checkboxClass(status: TaskItem["status"]): string {
+ switch (status) {
+ case "pending":
+ return "checkbox checkbox-sm rounded-sm checkbox-secondary";
+ case "in_progress":
+ return "checkbox checkbox-sm rounded-sm checkbox-info";
+ case "done":
+ return "checkbox checkbox-sm rounded-sm checkbox-success";
}
+}
+
+function isChecked(status: TaskItem["status"]): boolean {
+ return status === "done";
+}
- function statusIcon(status: TaskItem["status"]): string {
- switch (status) {
- case "pending":
- return "⏳";
- case "in_progress":
- return "▶";
- case "done":
- return "✓";
- case "blocked":
- return "⚠";
- }
+function isIndeterminate(status: TaskItem["status"]): boolean {
+ return status === "in_progress";
+}
+
+function statusLabel(status: TaskItem["status"]): string {
+ switch (status) {
+ case "pending":
+ return "Pending";
+ case "in_progress":
+ return "In progress";
+ case "done":
+ return "Done";
}
+}
</script>
<div class="flex flex-col gap-2">
@@ -43,28 +47,35 @@
<p class="text-xs text-base-content/50">No tasks yet.</p>
{:else}
<p class="text-xs text-base-content/60">
- {tasks.length} task{tasks.length !== 1 ? "s" : ""}
- ({doneCount} done, {inProgressCount} in progress)
+ {doneCount}/{tasks.length} done{#if inProgressCount > 0}, {inProgressCount} in progress{/if}
</p>
- <ul class="flex flex-col gap-1">
+ <ul class="flex flex-col gap-0.5">
{#each tasks as task (task.id)}
- <li class="flex flex-col gap-0.5 rounded p-1.5 hover:bg-base-200 transition-colors">
- <div class="flex items-center gap-1.5">
- <span class={badgeClass(task.status)}>
- {statusIcon(task.status)}
- </span>
+ <li
+ class="flex items-start gap-2 rounded p-1.5 transition-colors {task.status === 'done' ? 'opacity-60' : ''}"
+ >
+ <input
+ type="checkbox"
+ class={checkboxClass(task.status)}
+ checked={isChecked(task.status)}
+ indeterminate={isIndeterminate(task.status)}
+ disabled
+ tabindex="-1"
+ />
+ <div class="flex flex-col gap-0.5 min-w-0">
<span
- class="text-sm leading-tight {task.status === 'in_progress'
- ? 'font-bold'
- : 'font-medium'}"
+ class="text-xs leading-tight {task.status === 'done'
+ ? 'line-through text-base-content/50'
+ : task.status === 'in_progress'
+ ? 'font-semibold'
+ : ''}"
>
{task.title}
</span>
+ {#if task.description}
+ <p class="text-xs text-base-content/50 line-clamp-2">{task.description}</p>
+ {/if}
</div>
- {#if task.description}
- <p class="text-xs text-base-content/60 line-clamp-2 pl-5">{task.description}</p>
- {/if}
- <p class="text-xs text-base-content/30 pl-5 font-mono">{task.id}</p>
</li>
{/each}
</ul>