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
2 changes: 1 addition & 1 deletion frontend/docs/TRACEY.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ propagates the whole way down both planes:
renders a calm "Wait stopped" state (not a red error). It detects the stop two ways: assistant-ui
sometimes finalizes the part as `incomplete/cancelled`, but a Stop that lands mid-`execute` leaves
the aborted tool call **orphaned in `running`** (no terminal delta, no result), so the card also
treats a still-`running` part as stopped once the thread itself is idle (`useThread(t => t.isRunning)`)
treats a still-`running` part as stopped once the thread itself is idle (`useAuiState(s => s.thread.isRunning)`)
— otherwise it would spin on "Waiting for N actions" forever.

## Progressive tool disclosure (skills gate tools)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useComposerRuntime, useThread } from '@assistant-ui/react';
import { useAui, useAuiState } from '@assistant-ui/react';
import { useLingui } from '@lingui/react/macro';
import { Badge } from '../../../components/ui/Badge';
import { cn } from '../../../lib/cn';
Expand All @@ -19,9 +19,9 @@ const CHIP = cn(
export function FollowUpSuggestions() {
const { t } = useLingui();
const { followUps } = useTraceyChatContext();
const composer = useComposerRuntime();
const isRunning = useThread(thread => thread.isRunning);
const lastMessageId = useThread(thread => thread.messages.at(-1)?.id);
const composer = useAui().composer;
const isRunning = useAuiState(s => s.thread.isRunning);
const lastMessageId = useAuiState(s => s.thread.messages.at(-1)?.id);

if (!followUps || isRunning || followUps.messageId !== lastMessageId) return null;

Expand Down
6 changes: 3 additions & 3 deletions frontend/src/features/tracey/components/MessageStatusBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMessage } from '@assistant-ui/react';
import { useAuiState } from '@assistant-ui/react';
import { Trans, useLingui } from '@lingui/react/macro';
import { ClockIcon, CoinsIcon } from '../../../components/icons';
import { fmtLatency, fmtTokens } from '../../../lib/format';
Expand All @@ -19,8 +19,8 @@ export function MessageStatusBar() {
const { t } = useLingui();
// `metadata.custom` is a stable reference on the stored message, so selecting it (rather than a
// freshly-derived object) keeps the selector snapshot stable.
const custom = useMessage((m) => (m.role === 'assistant' ? m.metadata.custom : undefined));
const text = useMessage((m) =>
const custom = useAuiState(({ message: m }) => (m.role === 'assistant' ? m.metadata.custom : undefined));
const text = useAuiState(({ message: m }) =>
m.role === 'assistant' ? m.content.map((p) => (p.type === 'text' ? p.text : '')).join('').trim() : '',
);
const { openTrace } = useOpenResponseTrace();
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/features/tracey/components/ToolChips.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useComposerRuntime } from '@assistant-ui/react';
import { useAui } from '@assistant-ui/react';
import { useLingui } from '@lingui/react/macro';
import { Badge } from '../../../components/ui/Badge';
import { cn } from '../../../lib/cn';
Expand All @@ -11,7 +11,7 @@ const CHIP = cn(

/** Starter chips above the composer that surface available quick-actions; clicking one sends its prompt. */
export function ToolChips() {
const composer = useComposerRuntime();
const composer = useAui().composer;
const { i18n } = useLingui();

const sendPrompt = (prompt: string) => {
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/features/tracey/components/TraceyChatPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useThread } from '@assistant-ui/react';
import { useAuiState } from '@assistant-ui/react';
import { Trans, useLingui } from '@lingui/react/macro';
import { LayoutSidebarIcon, SparklesIcon } from '../../../components/icons';
import { IconButton } from '../../../components/ui/Button';
Expand All @@ -21,9 +21,9 @@ export function TraceyChatPanel({ chat, railCollapsed, onToggleRail }: TraceyCha
// Empty thread → "initial view": composer floats toward the middle with starter chips. The
// bottom spacer animates to 0 on the first message (and back when the conversation is cleared),
// which slides the composer down to / up from the bottom.
const isEmpty = useThread(t => t.messages.length === 0);
const isEmpty = useAuiState(s => s.thread.messages.length === 0);
// The header halo spins fast while a turn runs — the panel-level "Tracey is working" signal.
const isRunning = useThread(t => t.isRunning);
const isRunning = useAuiState(s => s.thread.isRunning);

return (
<div className="flex min-h-0 flex-1 flex-col rounded-lg border border-border bg-surface-2">
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/features/tracey/components/TraceyComposer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo, useState, type KeyboardEvent } from 'react';
import { ComposerPrimitive, ThreadPrimitive, useComposer, useComposerRuntime, useThread } from '@assistant-ui/react';
import { ComposerPrimitive, ThreadPrimitive, useAui, useAuiState } from '@assistant-ui/react';
import { Trans, useLingui } from '@lingui/react/macro';
import { type I18n } from '@lingui/core';
import { QUICK_ACTIONS } from '../tracey-quick-actions';
Expand Down Expand Up @@ -44,11 +44,11 @@ function matches(item: SlashItem, query: string, i18n: I18n): boolean {

export function TraceyComposer({ onNewConversation, showStarters }: TraceyComposerProps) {
const { t, i18n } = useLingui();
const composer = useComposerRuntime();
const text = useComposer(c => c.text);
const composer = useAui().composer;
const text = useAuiState(s => s.composer.text);
// While a turn streams/runs tools the composer frame carries the animated streaming ring
// (DESIGN.md §8) so the "Tracey is working" signal lives right where the user's attention is.
const isRunning = useThread(thread => thread.isRunning);
const isRunning = useAuiState(s => s.thread.isRunning);
// The selection is tagged with the list ("key") it belongs to. When the menu (re)opens or the
// query changes, that key changes and the highlight derives back to the first entry — so
// keyboard nav always starts from a known position without an effect.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ToolCallMessagePartComponent, useThread } from '@assistant-ui/react';
import { type ToolCallMessagePartComponent, useAuiState } from '@assistant-ui/react';
import { Trans, useLingui } from '@lingui/react/macro';
import { msg, plural } from '@lingui/core/macro';
import { type MessageDescriptor } from '@lingui/core';
Expand Down Expand Up @@ -40,7 +40,7 @@ const OUTCOME_LABEL: Record<AwaitOutcomeTone, MessageDescriptor> = {
*/
export const AwaitActionsToolUI: ToolCallMessagePartComponent = ({ args, result, status, isError }) => {
const { t, i18n } = useLingui();
const threadRunning = useThread((thread) => thread.isRunning);
const threadRunning = useAuiState((s) => s.thread.isRunning);
const resolved = result as AwaitActionsResult | undefined;
// User hit Stop while the wait was polling: the poll aborts, but the test run / theory keeps
// running on the backend — so this is a calm "stopped", not a red error. Two shapes reach here:
Expand Down
Loading