This commit is contained in:
Timothy Jaeryang Baek
2026-07-14 00:10:28 -04:00
parent 0c23466a3e
commit 7088d245bb
18 changed files with 1329 additions and 211 deletions
@@ -0,0 +1,67 @@
<script lang="ts">
import { getContext } from 'svelte';
import type { Readable } from 'svelte/store';
import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
type SubagentResult = {
delegation_id?: string;
delegation_ids?: string[];
subagent_chat_id?: string;
subagent_chat_ids?: string[];
};
const i18n = getContext<Readable<{ t: (value: string) => string }>>('i18n');
export let content: string;
export let result: SubagentResult;
let expanded = false;
$: delegationIds = Array.isArray(result.delegation_ids) ? result.delegation_ids : [];
$: delegationLabel =
result.delegation_id ?? (delegationIds.length > 1 ? `${delegationIds.length} tasks` : '');
$: summary = (() => {
const line = content
.split('\n')
.map((value) => value.trim())
.find((value) => value && !value.startsWith('['));
if (!line) return delegationLabel;
return line.length > 96 ? `${line.slice(0, 96)}...` : line;
})();
</script>
<div class="w-full min-w-0 pb-1">
<button
type="button"
class="w-full min-w-0 flex items-center gap-2 text-left text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-colors"
aria-expanded={expanded}
on:click={() => (expanded = !expanded)}
>
<span class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 shrink-0"></span>
<span class="text-[0.75rem] font-medium shrink-0">
{$i18n.t('Background sub-agent finished')}
</span>
{#if summary}
<span class="text-[0.75rem] truncate min-w-0 flex-1">{summary}</span>
{/if}
{#if delegationLabel}
<span
class="hidden sm:inline text-[0.6875rem] font-mono text-gray-400 dark:text-gray-600 shrink-0"
>
{delegationLabel}
</span>
{/if}
<ChevronDown
className="size-3 text-gray-400 dark:text-gray-600 shrink-0 transition-transform duration-150 {expanded
? 'rotate-180'
: ''}"
/>
</button>
{#if expanded}
<div
class="mt-2 ml-3 border-l border-gray-100 dark:border-white/8 pl-3 text-[0.78125rem] leading-relaxed text-gray-600 dark:text-gray-400 whitespace-pre-wrap break-words"
>
{content}
</div>
{/if}
</div>
@@ -16,11 +16,19 @@
import Markdown from './Markdown.svelte';
import Image from '$lib/components/common/Image.svelte';
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import SubagentResultRow from './SubagentResultRow.svelte';
import localizedFormat from 'dayjs/plugin/localizedFormat';
const i18n = getContext('i18n');
dayjs.extend(localizedFormat);
type SubagentResult = {
async_subagent_result: true;
delegation_id?: string;
delegation_ids?: string[];
subagent_chat_id?: string;
subagent_chat_ids?: string[];
};
export let user;
@@ -54,6 +62,7 @@
let editScrollContainer: HTMLDivElement;
let message = structuredClone(history.messages[messageId]);
let subagentResult: SubagentResult | undefined;
$: if (history.messages) {
const source = history.messages[messageId];
if (source) {
@@ -64,6 +73,7 @@
}
}
}
$: subagentResult = message?.meta?.async_subagent_result ? message.meta : undefined;
const copyToClipboard = async (text) => {
const res = await _copyToClipboard(text);
@@ -133,7 +143,7 @@
id="message-{message.id}"
style="scroll-margin-top: 3rem;"
>
{#if !($settings?.chatBubble ?? true)}
{#if !($settings?.chatBubble ?? true) && !subagentResult}
<div class={`shrink-0 ltr:mr-3 rtl:ml-3 mt-1`}>
<ProfileImage
src={user?.id
@@ -143,8 +153,8 @@
/>
</div>
{/if}
<div class="flex-auto w-0 max-w-full pl-1">
{#if !($settings?.chatBubble ?? true)}
<div class="flex-auto w-0 max-w-full {subagentResult ? '' : 'pl-1'}">
{#if !($settings?.chatBubble ?? true) && !subagentResult}
<div>
<Name>
{#if message.user}
@@ -179,7 +189,7 @@
{/if}
</Name>
</div>
{:else if message.timestamp}
{:else if message.timestamp && !subagentResult}
<div class="flex justify-end pr-2 text-xs">
<div
class="text-[0.65rem] font-medium first-letter:capitalize mb-0.5 {($settings?.highContrastMode ??
@@ -365,6 +375,8 @@
</div>
</div>
</div>
{:else if subagentResult}
<SubagentResultRow content={message.content} result={subagentResult} />
{:else if message.content !== ''}
<div class="w-full">
<div class="flex {($settings?.chatBubble ?? true) ? 'justify-end pb-1' : 'w-full'}">
@@ -394,7 +406,7 @@
</div>
{/if}
{#if edit !== true}
{#if edit !== true && !subagentResult}
<div
class=" flex {($settings?.chatBubble ?? true)
? 'justify-end'
@@ -127,6 +127,20 @@ function buildToolCallToken(item: OutputItem, toolOutputByCallId: Record<string,
const callId = item.call_id ?? '';
const resultItem = toolOutputByCallId[callId];
const isDone = isDoneStatus(item.status) || !!resultItem;
let name = item.name ?? '';
if (name === 'delegate_task') {
try {
const args =
typeof item.arguments === 'string'
? JSON.parse(item.arguments || '{}')
: (item.arguments ?? {});
const task = typeof args.task === 'string' && args.task ? args.task : '?';
const label = args.background ? 'Background sub-agent' : 'Sub-agent';
name = `${label}: "${task.length > 60 ? `${task.slice(0, 60)}...` : task}"`;
} catch {
name = 'Sub-agent';
}
}
return {
summary: isDone ? 'Tool Executed' : 'Executing...',
@@ -134,7 +148,7 @@ function buildToolCallToken(item: OutputItem, toolOutputByCallId: Record<string,
attributes: {
type: 'tool_calls',
id: callId,
name: item.name ?? '',
name,
done: isDone ? 'true' : 'false',
arguments: stringifyAttribute(item.arguments ?? ''),
files: stringifyAttribute(resultItem?.files),