| ... | @@ -28,7 +28,7 @@ import { | ... | @@ -28,7 +28,7 @@ import { |
| 28 | type ServerProvider, | 28 | type ServerProvider, |
| 29 | type ServerProviderSkill, | 29 | type ServerProviderSkill, |
| 30 | type RunId, | 30 | type RunId, |
| 31 | type ThreadId, | 31 | ThreadId, |
| 32 | type ToolActivityIcon, | 32 | type ToolActivityIcon, |
| 33 | } from "@t3tools/contracts"; | 33 | } from "@t3tools/contracts"; |
| 34 | import { parseScopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime/environment"; | 34 | import { parseScopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime/environment"; |
| ... | @@ -208,6 +208,9 @@ import { | ... | @@ -208,6 +208,9 @@ import { |
| 208 | resolveWorkGroupScrollIndex, | 208 | resolveWorkGroupScrollIndex, |
| 209 | shouldFollowWorkGroupAppend, | 209 | shouldFollowWorkGroupAppend, |
| 210 | shouldPreserveAssistantLineBreaks, | 210 | shouldPreserveAssistantLineBreaks, |
| | 211 | threadReadLabelPrefix, |
| | 212 | threadReadTargetId, |
| | 213 | threadReadTargetTitle, |
| 211 | toolGroupAction, | 214 | toolGroupAction, |
| 212 | workEntryDisplayLabel, | 215 | workEntryDisplayLabel, |
| 213 | workEntryReadOutput, | 216 | workEntryReadOutput, |
| ... | @@ -3722,19 +3725,67 @@ function LiveActivityContent({ | ... | @@ -3722,19 +3725,67 @@ function LiveActivityContent({ |
| 3722 | ); | 3725 | ); |
| 3723 | } | 3726 | } |
| 3724 | | 3727 | |
| | 3728 | /** The thread a `t3_thread_read` call targets, titled from live shell state so renames show. */ |
| | 3729 | function useThreadReadTarget(entry: TimelineWorkEntry, environmentId: EnvironmentId) { |
| | 3730 | const rawThreadId = threadReadTargetId(entry); |
| | 3731 | const threadId = rawThreadId === null ? null : ThreadId.make(rawThreadId); |
| | 3732 | const shell = useThreadShell(threadId ? scopeThreadRef(environmentId, threadId) : null); |
| | 3733 | const title = threadReadTargetTitle(shell); |
| | 3734 | return threadId && title ? { threadId, title } : null; |
| | 3735 | } |
| | 3736 | |
| | 3737 | function threadReadLabel(label: string, target: ReturnType<typeof useThreadReadTarget>) { |
| | 3738 | const prefix = target && threadReadLabelPrefix(label); |
| | 3739 | return prefix ? { ...target, prefix, text: `${prefix} “${target.title}”` } : null; |
| | 3740 | } |
| | 3741 | |
| | 3742 | /** Only settled rows link the title; the live row is itself a button. */ |
| | 3743 | function ThreadReadLabel({ |
| | 3744 | label, |
| | 3745 | environmentId, |
| | 3746 | linked, |
| | 3747 | }: { |
| | 3748 | label: NonNullable<ReturnType<typeof threadReadLabel>>; |
| | 3749 | environmentId: EnvironmentId; |
| | 3750 | linked: boolean; |
| | 3751 | }) { |
| | 3752 | return ( |
| | 3753 | <span className="flex min-w-0"> |
| | 3754 | <span className="shrink-0">{label.prefix} “</span> |
| | 3755 | {linked ? ( |
| | 3756 | <Link |
| | 3757 | to="/$environmentId/$threadId" |
| | 3758 | params={{ environmentId, threadId: label.threadId }} |
| | 3759 | className="min-w-0 truncate rounded-sm text-foreground underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-ring" |
| | 3760 | onClick={stopRowToggle} |
| | 3761 | onKeyDown={stopRowToggle} |
| | 3762 | > |
| | 3763 | {label.title} |
| | 3764 | </Link> |
| | 3765 | ) : ( |
| | 3766 | <span className="min-w-0 truncate">{label.title}</span> |
| | 3767 | )} |
| | 3768 | <span className="shrink-0">”</span> |
| | 3769 | </span> |
| | 3770 | ); |
| | 3771 | } |
| | 3772 | |
| 3725 | function LiveWorkEntryTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "work-live" }> }) { | 3773 | function LiveWorkEntryTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "work-live" }> }) { |
| 3726 | const ctx = use(TimelineRowCtx); | 3774 | const ctx = use(TimelineRowCtx); |
| | 3775 | const threadTarget = useThreadReadTarget(row.entry, ctx.activeThreadEnvironmentId); |
| 3727 | const questionHeading = row.entry.questionAnswer | 3776 | const questionHeading = row.entry.questionAnswer |
| 3728 | ? getQuestionTextPreview(row.entry.questionAnswer) | 3777 | ? getQuestionTextPreview(row.entry.questionAnswer) |
| 3729 | : ""; | 3778 | : ""; |
| 3730 | const label = questionHeading || liveWorkEntryLabel(row.entry, ctx.workspaceRoot, row.active); | 3779 | const label = questionHeading || liveWorkEntryLabel(row.entry, ctx.workspaceRoot, row.active); |
| | 3780 | const threadLabel = threadReadLabel(label, threadTarget); |
| | 3781 | |
| 3731 | const failed = workEntryDisplayIndicatesToolFailure(row.entry); | 3782 | const failed = workEntryDisplayIndicatesToolFailure(row.entry); |
| 3732 | | 3783 | |
| 3733 | return ( | 3784 | return ( |
| 3734 | <button | 3785 | <button |
| 3735 | type="button" | 3786 | type="button" |
| 3736 | className="group/live-work flex min-h-6 w-full max-w-full cursor-pointer items-center rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/70" | 3787 | className="group/live-work flex min-h-6 w-full max-w-full cursor-pointer items-center rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/70" |
| 3737 | aria-label={failed ? `${label}, tool call failed` : undefined} | 3788 | aria-label={failed ? `${threadLabel?.text ?? label}, tool call failed` : undefined} |
| 3738 | aria-expanded={row.expanded} | 3789 | aria-expanded={row.expanded} |
| 3739 | onClick={() => ctx.onToggleWorkGroup(row.groupId, row.id)} | 3790 | onClick={() => ctx.onToggleWorkGroup(row.groupId, row.id)} |
| 3740 | > | 3791 | > |
| ... | @@ -3756,6 +3807,12 @@ function LiveWorkEntryTimelineRow({ row }: { row: Extract<TimelineRow, { kind: " | ... | @@ -3756,6 +3807,12 @@ function LiveWorkEntryTimelineRow({ row }: { row: Extract<TimelineRow, { kind: " |
| 3756 | > | 3807 | > |
| 3757 | {row.entry.detail ?? label} | 3808 | {row.entry.detail ?? label} |
| 3758 | </ReactMarkdown> | 3809 | </ReactMarkdown> |
| | 3810 | ) : threadLabel ? ( |
| | 3811 | <ThreadReadLabel |
| | 3812 | label={threadLabel} |
| | 3813 | environmentId={ctx.activeThreadEnvironmentId} |
| | 3814 | linked={false} |
| | 3815 | /> |
| 3759 | ) : ( | 3816 | ) : ( |
| 3760 | label | 3817 | label |
| 3761 | ) | 3818 | ) |
| ... | @@ -5113,6 +5170,7 @@ function WorkEntryLogRow(props: WorkEntryRowProps) { | ... | @@ -5113,6 +5170,7 @@ function WorkEntryLogRow(props: WorkEntryRowProps) { |
| 5113 | const ctx = use(TimelineRowCtx); | 5170 | const ctx = use(TimelineRowCtx); |
| 5114 | const { threadRef, onImageExpand, timestampFormat } = ctx; | 5171 | const { threadRef, onImageExpand, timestampFormat } = ctx; |
| 5115 | const { retryableWorkspacePreparationRunIds, onRetryWorkspacePreparation } = ctx; | 5172 | const { retryableWorkspacePreparationRunIds, onRetryWorkspacePreparation } = ctx; |
| | 5173 | const threadTarget = useThreadReadTarget(workEntry, ctx.activeThreadEnvironmentId); |
| 5116 | const createdThread = | 5174 | const createdThread = |
| 5117 | workEntry.projectedItem?.item.type === "thread_created" | 5175 | workEntry.projectedItem?.item.type === "thread_created" |
| 5118 | ? workEntry.projectedItem.item | 5176 | ? workEntry.projectedItem.item |
| ... | @@ -5308,7 +5366,10 @@ function WorkEntryLogRow(props: WorkEntryRowProps) { | ... | @@ -5308,7 +5366,10 @@ function WorkEntryLogRow(props: WorkEntryRowProps) { |
| 5308 | : workLogEntryIsToolLike(workEntry) | 5366 | : workLogEntryIsToolLike(workEntry) |
| 5309 | ? "text-secondary-label" | 5367 | ? "text-secondary-label" |
| 5310 | : "text-foreground/80"; | 5368 | : "text-foreground/80"; |
| 5311 | const accessiblePreview = [previewText, answerPreview].filter(Boolean).join(": "); | 5369 | const threadLabel = isReasoning ? null : threadReadLabel(previewText, threadTarget); |
| | 5370 | const accessiblePreview = [threadLabel?.text ?? previewText, answerPreview] |
| | 5371 | .filter(Boolean) |
| | 5372 | .join(": "); |
| 5312 | const accessibleDisplayText = showFailedIndicator | 5373 | const accessibleDisplayText = showFailedIndicator |
| 5313 | ? `${accessiblePreview}, tool call failed` | 5374 | ? `${accessiblePreview}, tool call failed` |
| 5314 | : accessiblePreview; | 5375 | : accessiblePreview; |
| ... | @@ -5365,6 +5426,12 @@ function WorkEntryLogRow(props: WorkEntryRowProps) { | ... | @@ -5365,6 +5426,12 @@ function WorkEntryLogRow(props: WorkEntryRowProps) { |
| 5365 | > | 5426 | > |
| 5366 | {workEntry.detail ?? previewText} | 5427 | {workEntry.detail ?? previewText} |
| 5367 | </ReactMarkdown> | 5428 | </ReactMarkdown> |
| | 5429 | ) : threadLabel ? ( |
| | 5430 | <ThreadReadLabel |
| | 5431 | label={threadLabel} |
| | 5432 | environmentId={ctx.activeThreadEnvironmentId} |
| | 5433 | linked |
| | 5434 | /> |
| 5368 | ) : ( | 5435 | ) : ( |
| 5369 | previewText | 5436 | previewText |
| 5370 | )} | 5437 | )} |