Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
81 changes: 61 additions & 20 deletions interface/src/routes/ChannelDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface ChannelDetailProps {
onLoadMore: () => void;
}

function CancelButton({ onClick }: { onClick: () => void }) {
function CancelButton({ onClick, className }: { onClick: () => void; className?: string }) {
const [cancelling, setCancelling] = useState(false);
return (
<Button
Expand All @@ -32,7 +32,7 @@ function CancelButton({ onClick }: { onClick: () => void }) {
setCancelling(true);
onClick();
}}
className="ml-auto h-7 w-7 flex-shrink-0 text-ink-faint/50 hover:bg-red-500/15 hover:text-red-400"
className={`h-7 w-7 flex-shrink-0 text-ink-faint/50 hover:bg-red-500/15 hover:text-red-400 ${className ?? ""}`}
title="Cancel"
>
<HugeiconsIcon icon={Cancel01Icon} className="h-3.5 w-3.5" />
Expand All @@ -53,7 +53,7 @@ function LiveBranchRunItem({ item, live, channelId }: { item: TimelineBranchRun;
<div className="h-2 w-2 animate-pulse rounded-full bg-violet-400" />
<span className="text-sm font-medium text-violet-300">Branch</span>
<span className="min-w-0 flex-1 truncate text-sm text-ink-dull">{item.description}</span>
<CancelButton onClick={() => { api.cancelProcess(channelId, "branch", item.id).catch(console.warn); }} />
<CancelButton className="ml-auto" onClick={() => { api.cancelProcess(channelId, "branch", item.id).catch(console.warn); }} />
</div>
<div className="mt-1 flex items-center gap-3 pl-4 text-tiny text-ink-faint">
<LiveDuration startMs={live.startedAt} />
Expand All @@ -71,27 +71,46 @@ function LiveBranchRunItem({ item, live, channelId }: { item: TimelineBranchRun;
}

function LiveWorkerRunItem({ item, live, channelId, agentId }: { item: TimelineWorkerRun; live: ActiveWorker; channelId: string; agentId: string }) {
const [expanded, setExpanded] = useState(true);

return (
<div className="flex gap-3 px-3 py-2">
<span className="flex-shrink-0 pt-0.5 text-tiny text-ink-faint">
{formatTimestamp(new Date(item.started_at).getTime())}
</span>
<div className="min-w-0 flex-1">
<div className="rounded-md bg-amber-500/10 px-3 py-2 transition-colors hover:bg-amber-500/15">
<div className="w-full rounded-md bg-amber-500/10 px-3 py-2 transition-colors hover:bg-amber-500/15">
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
<div className="h-2 w-2 animate-pulse rounded-full bg-amber-400" />
<span className="text-sm font-medium text-amber-300">Worker</span>
<button
type="button"
onClick={() => setExpanded(!expanded)}
className="min-w-0 flex-1 text-left"
>
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
<div className="h-2 w-2 animate-pulse rounded-full bg-amber-400" />
<span className="text-sm font-medium text-amber-300">Worker</span>
<span className={`min-w-0 flex-1 text-sm text-ink-dull ${
expanded ? "whitespace-normal break-words" : "truncate"
}`}>{item.task}</span>
<span className="flex-shrink-0 text-tiny leading-5 text-ink-faint">
{expanded ? "▾" : "▸"}
</span>
</div>
</button>
<Link
to="/agents/$agentId/workers"
params={{ agentId }}
search={{ worker: item.id }}
className="min-w-0 flex-1 truncate text-sm text-ink-dull hover:text-ink"
className="flex-shrink-0 rounded border border-amber-400/30 px-1.5 py-0.5 text-tiny font-medium text-amber-300 transition-colors hover:border-amber-400/60 hover:bg-amber-500/15"
>
{item.task}
Open
</Link>
<CancelButton onClick={() => { api.cancelProcess(channelId, "worker", item.id).catch(console.warn); }} />
</div>
</div>
{expanded && (
<div className="mt-1 flex min-w-0 items-center gap-3 overflow-hidden pl-4 text-tiny text-ink-faint">
<LiveDuration startMs={live.startedAt} />
<span className="truncate">{live.status}</span>
{live.currentTool && (
<span className="truncate text-amber-400/70">{live.currentTool}</span>
Expand All @@ -100,7 +119,7 @@ function LiveWorkerRunItem({ item, live, channelId, agentId }: { item: TimelineW
<span>{live.toolCalls} tool calls</span>
)}
</div>
</div>
)}
</div>
</div>
);
Expand Down Expand Up @@ -151,25 +170,47 @@ function BranchRunItem({ item }: { item: TimelineBranchRun }) {
}

function WorkerRunItem({ item, agentId }: { item: TimelineWorkerRun; agentId: string }) {
const [expanded, setExpanded] = useState(false);

return (
<div className="flex gap-3 px-3 py-2">
<span className="flex-shrink-0 pt-0.5 text-tiny text-ink-faint">
{formatTimestamp(new Date(item.started_at).getTime())}
</span>
<div className="min-w-0 flex-1">
<Link
to="/agents/$agentId/workers"
params={{ agentId }}
search={{ worker: item.id }}
className="block w-full rounded-md bg-amber-500/10 px-3 py-2 text-left transition-colors hover:bg-amber-500/15"
>
<div className="w-full rounded-md bg-amber-500/10 px-3 py-2 transition-colors hover:bg-amber-500/15">
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
<div className="h-2 w-2 rounded-full bg-amber-400/50" />
<span className="text-sm font-medium text-amber-300">Worker</span>
<span className="min-w-0 flex-1 truncate text-sm text-ink-dull">{item.task}</span>
<button
type="button"
onClick={() => {
if (item.result) setExpanded(!expanded);
}}
className="min-w-0 flex-1 text-left"
>
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
<div className="h-2 w-2 rounded-full bg-amber-400/50" />
<span className="text-sm font-medium text-amber-300">Worker</span>
<span className={`min-w-0 flex-1 text-sm text-ink-dull ${
expanded ? "whitespace-normal break-words" : "truncate"
}`}>{item.task}</span>
{item.result && (
<span className="flex-shrink-0 text-tiny leading-5 text-ink-faint">
{expanded ? "▾" : "▸"}
</span>
)}
</div>
</button>
<Link
to="/agents/$agentId/workers"
params={{ agentId }}
search={{ worker: item.id }}
className="flex-shrink-0 rounded border border-amber-400/30 px-1.5 py-0.5 text-tiny font-medium text-amber-300 transition-colors hover:border-amber-400/60 hover:bg-amber-500/15"
>
Open
</Link>
</div>
</Link>
{item.result && (
</div>
{expanded && item.result && (
<div className="mt-1 rounded-md border border-amber-500/10 bg-amber-500/5 px-3 py-2">
<div className="text-sm text-ink-dull">
<Markdown className="whitespace-pre-wrap break-words">{item.result}</Markdown>
Expand Down
4 changes: 1 addition & 3 deletions prompts/en/channel.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ You have a soul, an identity, and a personality. These are loaded separately and

Every turn, you receive the user's message along with a live status block showing active workers, branches, and recently completed work. Use this to stay aware of what's happening without asking.

When a branch result arrives, it appears as a distinct message in your history — a conclusion from a thought process you initiated. Incorporate it naturally. The user doesn't need to know about the internal process unless it's relevant.

When a worker completes, its result appears as a "[Worker completed]" message in your history. The user cannot see this message — you must relay the result to them using the reply tool. Include the actual substance and details, not just a summary teaser. If it's `notify: false`, it's background work — don't mention it unless the user asks.
When a background process (branch or worker) completes, you will receive a system message containing the full result text, tagged with the process type and ID. The user has NOT seen any of it — you must relay the substance to them using the reply tool. Include actual content and details, not just a summary teaser. Do not mention internal processes (branch, worker, process IDs). If a result is background work the user didn't ask about, incorporate it silently.

You are able to write code or do work extremely fast inside a worker, never say something will take too long, just do it. You are an agentic coding machine.

Expand Down
13 changes: 12 additions & 1 deletion prompts/en/fragments/system/retrigger.md.j2
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
[System: a background process has completed. The result appears in your history as a "[Worker completed]" or "[Branch result]" message — the user has NOT seen any of it. You must relay the full substance of the result using the reply tool. Include the actual content, data, and details from the result in your reply — do not just reference or tease it. Do not mention internal processes (branch, worker, retrigger, status block). Only skip if the result is genuinely empty or irrelevant to anything the user asked about.]
[System: {{ results | length }} background process(es) just completed. The user has NOT seen any of the results below. You MUST relay the full substance to the user using the reply tool.

{% for r in results %}
--- {{ r.process_type }} {{ r.process_id }}{% if not r.success %} (FAILED){% endif %} ---
{{ r.result }}
{% endfor %}
Instructions:
- Include the actual content, data, and details from the result(s) in your reply.
- Do not just reference or tease the results — relay them fully.
- Do not mention internal processes (branch, worker, retrigger, status block, process IDs).
- If a result failed, let the user know what went wrong in plain language.
- Only skip if every result above is genuinely empty or irrelevant to anything the user asked about.]
Loading