Skip to content
Open
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/app/lab/agent-screen-showcase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function AgentScreenShowcase() {
agentName="Nova"
status="loading"
loading
loadingCaption="No active sandbox. Send a message to start one."
loadingCaption="Nothing is running yet. Send a message to start one."
screen={null}
open={false}
onOpenChange={() => {}}
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat/agent-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export function AgentDetail({
onClick={() => void continueAsSession()}
className="border-border-button-default bg-background-secondary-default text-body-2-medium text-text-primary hover:bg-background-tertiary-hover mb-2 rounded-lg border px-3 py-2 disabled:cursor-wait disabled:opacity-60"
>
{continuing ? "Creating session…" : "Continue as session"}
{continuing ? "Creating thread…" : "Continue as thread"}
</button>
) : null}
{continueError ? (
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat/agents-rail.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ describe("agents rail rows", () => {
expect(html).toContain("Checkout is healthy.");
expect(html).toContain("Inspect the checkout flow");
expect(html).toContain("Completed");
expect(html).toContain("Continue as session");
expect(html).toContain("Continue as thread");
expect(html).not.toContain("Working");
expect(html).toContain("cancel</span> unavailable: This child is no longer running.");
expect(html).not.toContain("No activity recorded");
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat/agents-rail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ export function AgentsRail({
<div
ref={treeRef}
role="tree"
aria-label="Child sessions"
aria-label="Subagents"
aria-live="polite"
aria-relevant="additions text"
className="h-full space-y-2 overflow-y-auto p-3"
Expand Down
6 changes: 3 additions & 3 deletions frontend/components/chat/artifacts-rail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ export function ArtifactsRail({
<div className="absolute inset-0 overflow-y-auto p-3">
<div className="mb-3 flex items-center justify-between gap-3">
<div className="min-w-0">
<p className="text-body-2-medium text-text-primary">Session files</p>
<p className="text-body-2-medium text-text-primary">Files</p>
<p className="text-caption-1-regular text-text-tertiary">
Durable outputs you can preview, edit, or download
Finished files like a report or spreadsheet. Preview, edit, or download them.
</p>
</div>
<IconButton
size="small"
icon={refreshing ? SpinningRefreshIcon : RiRefreshLine}
onClick={() => void load()}
disabled={refreshing}
aria-label="Refresh session files"
aria-label="Refresh files"
title="Refresh"
className="shrink-0"
/>
Expand Down
15 changes: 7 additions & 8 deletions frontend/components/chat/desktop-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ export const DESKTOP_PROBE_MAX_DELAY = 2_000;
/** Product copy for one readiness probe answer. A sandbox image that lacks the
* desktop binaries never becomes ready, so name them instead of waiting. */
export function desktopProbeStatus(status: number, error: string | null): string {
if (status === 409) return "No active sandbox. Send a message to start one.";
if (status === 409) return "Nothing is running yet. Send a message to start one.";
const missing = /missing desktop binaries:\s*(.+)$/i.exec(error ?? "")?.[1]?.trim();
if (missing) {
return `Browser is unavailable on this sandbox image: it is missing ${missing.split(/\s+/).join(", ")}. Rebuild the image with those packages to enable it.`;
return `The browser is not available here: it is missing ${missing.split(/\s+/).join(", ")}. Rebuild the environment with those packages to enable it.`;
}
return "Starting sandbox desktop…";
return "Starting the browser…";
}

export function nextDesktopProbeDelay(previous: number | null): number {
Expand Down Expand Up @@ -151,7 +151,7 @@ export function DesktopPane({
const [frameKey, setFrameKey] = useState(0);
const [inputCaptured, setInputCaptured] = useState(false);
const [viewerOpen, setViewerOpen] = useState(false);
const [status, setStatus] = useState("No active sandbox. Send a message to start one.");
const [status, setStatus] = useState("Nothing is running yet. Send a message to start one.");
// The Agent Screen stage: the frame plus, while expanded, the viewer chrome.
// Pointer and focus activity inside it never releases captured input.
const surfaceRef = useRef<HTMLDivElement>(null);
Expand All @@ -177,7 +177,7 @@ export function DesktopPane({
setFrameConnected(false);
inputCapturedRef.current = false;
setInputCaptured(false);
setStatus("No active sandbox. Send a message to start one.");
setStatus("Nothing is running yet. Send a message to start one.");

const probe = async (): Promise<void> => {
try {
Expand All @@ -198,7 +198,7 @@ export function DesktopPane({
);
} catch {
if (cancelled) return;
setStatus("Reconnecting to sandbox desktop…");
setStatus("Reconnecting…");
}
delay = nextDesktopProbeDelay(delay);
retry = setTimeout(() => void probe(), delay);
Expand Down Expand Up @@ -349,7 +349,6 @@ export function DesktopPane({
<Button
variant="secondary"
size="small"
aria-label="Control sandbox desktop"
aria-pressed={inputCaptured}
disabled={!connected}
onClick={inputCaptured ? releaseCapture : captureInput}
Expand All @@ -364,7 +363,7 @@ export function DesktopPane({
key={frameKey}
ref={frameRef}
data-testid="desktop-frame"
title="Sandbox desktop"
title="Browser"
src={src}
sandbox={DESKTOP_FRAME_SANDBOX}
tabIndex={-1}
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/desktop-screen-state.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ describe("agent screen card", () => {
const html = render(false, {
status: "loading",
loading: true,
loadingCaption: "No active sandbox. Send a message to start one.",
loadingCaption: "Nothing is running yet. Send a message to start one.",
});
expect(html).toContain("Connecting to agent&#x27;s screen");
expect(html).toContain("No active sandbox. Send a message to start one.");
expect(html).toContain("Nothing is running yet. Send a message to start one.");
expect(html).toContain('data-status="loading"');
// Nothing to open into yet: the connecting card offers no Open control.
expect(html).not.toContain('aria-label="Open Nova&#x27;s screen"');
Expand Down
15 changes: 9 additions & 6 deletions frontend/components/chat/desktop-surface.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe("Desktop product surface", () => {

expect(desktopPane).toContain(`\`/api/desktop-proxy/${threadInterpolation}/ready\``);
expect(desktopPane).toContain('fetch(readySrc, { cache: "no-store" })');
expect(desktopPane).toContain("Starting sandbox desktop…");
expect(desktopPane).toContain("Starting the browser…");
// Retries on bounded exponential backoff, not a fixed one-second interval.
expect(desktopPane).toContain("delay = nextDesktopProbeDelay(delay)");
expect(desktopPane).toContain("setTimeout(() => void probe(), delay)");
Expand Down Expand Up @@ -129,7 +129,10 @@ describe("Desktop product surface", () => {
expect(desktopPane).toContain("desktopFrameInteractive({ loaded, captured: inputCaptured })");
expect(desktopPane).toContain('pointerEvents: frameInteractive ? "auto" : "none"');
expect(desktopPane).toContain("interactive={frameInteractive}");
expect(desktopPane).toContain('aria-label="Control sandbox desktop"');
// The control button has no fixed aria-label: its visible text (Take
// control / Release control) is its accessible name.
expect(desktopPane).not.toContain('aria-label="Control sandbox desktop"');
expect(desktopPane).toContain('inputCaptured ? "Release control" : "Take control"');
expect(desktopPane).toContain('window.addEventListener("focusin", releaseDesktopInput, true)');
expect(desktopPane).toContain(
'window.addEventListener("pointerdown", releaseDesktopInput, true)',
Expand Down Expand Up @@ -302,7 +305,7 @@ describe("Desktop probe copy", () => {
expect(
desktopProbeStatus(502, "desktop proxy failed: missing desktop binaries: budgie-panel"),
).toBe(
"Browser is unavailable on this sandbox image: it is missing budgie-panel. Rebuild the image with those packages to enable it.",
"The browser is not available here: it is missing budgie-panel. Rebuild the environment with those packages to enable it.",
);
expect(
desktopProbeStatus(
Expand All @@ -314,12 +317,12 @@ describe("Desktop probe copy", () => {

test("keeps the waiting and no-sandbox states for every other answer", () => {
expect(desktopProbeStatus(409, "no live sandbox for this conversation yet")).toBe(
"No active sandbox. Send a message to start one.",
"Nothing is running yet. Send a message to start one.",
);
expect(desktopProbeStatus(502, "desktop proxy failed: connect ECONNREFUSED")).toBe(
"Starting sandbox desktop…",
"Starting the browser…",
);
expect(desktopProbeStatus(500, null)).toBe("Starting sandbox desktop…");
expect(desktopProbeStatus(500, null)).toBe("Starting the browser…");
});

test("the pane reads the probe body so the copy can be honest", () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat/editor-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function FileDetail({ entry }: { entry: FileEntry }) {
filename={entry.base}
language={langForFile(entry.base)}
code={entry.content ?? ""}
emptyLabel="Content capture lands with the engine event mirror."
emptyLabel="This file's contents will show here once the agent saves it."
/>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/interactive-terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ export function InteractiveTerminal({ runId }: { runId: string }) {
waitingShown = true;
term?.write(
idleNoticeSeen
? "\r\n\x1b[2m• no active sandbox - send a message to start one…\x1b[0m\r\n"
: "\r\n\x1b[2m• reconnecting to sandbox…\x1b[0m\r\n",
? "\r\n\x1b[2m• nothing is running - send a message to start one…\x1b[0m\r\n"
: "\r\n\x1b[2m• reconnecting…\x1b[0m\r\n",
);
}
reconnectTimer = setTimeout(connect, reconnectDelay);
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat/session-rail-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function SessionRailTabs({
);
return (
<PillTabList
aria-label="Surface"
aria-label="Panels"
className="min-w-0 flex-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
>
{hasSubagents && tab("agents", RiRobot2Line, "Agents")}
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/session-thread-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ export function SessionThreadActions({
iconOnly
leadingIcon={RiLayoutRightLine}
onClick={onOpenSurfaces}
title="Open surfaces panel"
aria-label="Open surfaces panel"
title="Open side panel"
aria-label="Open side panel"
className={cx(RAIL_ICON_BUTTON, !splitTooNarrow && "md:hidden")}
/>
)}
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/session-thread-breadcrumb.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ test("a bot thread's own crumb is the bot's name", () => {
expect(html).not.toContain(">Nova: @bot/Nova compare the EU tiers<");
});

test("an ordinary root keeps the compact session label", () => {
test("an ordinary root keeps the compact thread label", () => {
expect(renderToStaticMarkup(
<SessionThreadBreadcrumb relationship={relationship("root", null, "Calendar app")} parent={null} />,
)).toContain("Session");
)).toContain("Thread");
});
2 changes: 1 addition & 1 deletion frontend/components/chat/session-thread-breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function SessionThreadBreadcrumb({
readonly parent: ThreadRelationship | null;
}) {
if (!relationship?.parentThreadId) {
return <span className="text-mono-label text-text-tertiary">Session</span>;
return <span className="text-mono-label text-text-tertiary">Thread</span>;
}
// Two truncated titles read as near-duplicates ("@bot/Nova compare..." /
// "Nova: @bot/Nova compare..."), so the parent crumb is a fixed word carrying
Expand Down
6 changes: 3 additions & 3 deletions frontend/components/chat/session-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ export function SessionView({ initialThread, initialOutline = null, initialRelat
)}
resourceMentions={!isProductChild}
composerLocked={composerRelationshipBlocked || readOnlyMessage !== undefined}
composerLockedMessage={readOnlyMessage ?? "Verifying child session…"}
composerLockedMessage={readOnlyMessage ?? "Verifying this thread…"}
onTurnsNeeded={initialOutline ? handleTurnsNeeded : undefined}
productChildren={productChildren}
onOpenProductChild={openProductChild}
Expand Down Expand Up @@ -773,7 +773,7 @@ export function SessionView({ initialThread, initialOutline = null, initialRelat
{railOpen && sheetSurfacesOpen && (
<button
type="button"
aria-label="Close surfaces panel"
aria-label="Close side panel"
onClick={() => setRailOverride(false)}
className={cx(
"fixed inset-0 z-40 bg-black/50 backdrop-blur-sm",
Expand Down Expand Up @@ -873,7 +873,7 @@ export function SessionView({ initialThread, initialOutline = null, initialRelat
{/* On the sheet a single Close X replaces Expand/Collapse. */}
<CloseButton
size="md"
aria-label="Close surfaces panel"
aria-label="Close side panel"
onClick={() => setRailOverride(false)}
className={cx(RAIL_ICON_BUTTON, !splitTooNarrow && "md:hidden")}
/>
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/subagent-pane-body.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe("run peek body", () => {
])("renders a %s as a session without child controls", (_name, initialRun) => {
const html = renderToStaticMarkup(<LoadedPane initialRun={initialRun} />);

expect(html).toContain(">Session<");
expect(html).toContain(">Thread<");
expect(html).toContain('href="/session/root"');
expect(html).toContain("Open thread");
expect(html).not.toContain(">Subagent<");
Expand All @@ -64,7 +64,7 @@ describe("run peek body", () => {
});

test("loading and error shells are neutral unless the caller knows it is a child", () => {
expect(renderToStaticMarkup(<PaneStub>Loading</PaneStub>)).toContain(">Session<");
expect(renderToStaticMarkup(<PaneStub>Loading</PaneStub>)).toContain(">Thread<");
expect(renderToStaticMarkup(<PaneStub label="Run">Loading</PaneStub>)).toContain(">Run<");
expect(renderToStaticMarkup(<PaneStub childSession>Loading</PaneStub>)).toContain(
">Subagent<",
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat/subagent-pane-body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export function LoadedPane({ initialRun }: { initialRun: ApiRun }) {
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1.5">
<span className="text-mono-label text-text-tertiary">
{childSession ? "Subagent" : "Session"}
{childSession ? "Subagent" : "Thread"}
</span>
<span className="text-text-tertiary">·</span>
<span className="text-mono-label text-text-tertiary">
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/subagent-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export function PaneStub({
<>
<header className="border-border-button-default flex shrink-0 items-center gap-2 border-b px-4 py-3">
<span className="text-mono-label text-text-tertiary">
{label ?? (childSession ? "Subagent" : "Session")}
{label ?? (childSession ? "Subagent" : "Thread")}
</span>
<span className="ml-auto" />
<CloseButton />
Expand Down Expand Up @@ -155,7 +155,7 @@ export function SubagentPane() {
return createPortal(
<aside
aria-hidden={!open}
aria-label={`${target?.childSession ? "Subagent" : "Session"} pane`}
aria-label={`${target?.childSession ? "Subagent" : "Thread"} pane`}
className={cn(
"border-border-button-default bg-background-primary-default shadow-sidebar fixed inset-y-0 right-0 z-40 flex h-dvh w-[440px] max-w-[92vw] flex-col border-l",
"transition-transform duration-300 ease-out",
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/surface-chooser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const SURFACES: readonly SurfaceOption[] = [
export function railTabLabelFor(
railTab: SurfaceChoice | "editor" | "workspace" | null,
): string {
if (railTab === null) return "Surface";
if (railTab === null) return "Panels";
const labels = {
agents: "Agents",
artifacts: "Files",
Expand Down Expand Up @@ -114,7 +114,7 @@ export function SurfaceChooser({
return (
<div className="flex h-full justify-center overflow-y-auto px-6 py-10">
<div className="w-full max-w-md">
<h2 className="sr-only">Open a surface</h2>
<h2 className="sr-only">Open a panel</h2>
{facts.length > 0 && (
<ul className="mb-6 flex flex-wrap gap-1.5" aria-label="This thread">
{facts.map((fact) => (
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/chat/terminal-pane.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ describe("TerminalPane log", () => {
expect(html).not.toContain("terminal-log-working");
});

test("empty log reads as booting while live, idle when settled", () => {
expect(render([], true)).toContain("Booting session");
test("empty log reads as starting while live, idle when settled", () => {
expect(render([], true)).toContain("Starting up");
expect(render([], false)).toContain("No commands were run.");
// A boot-phase live run with zero commands does not also show the footer.
expect(render([], true)).not.toContain("terminal-log-working");
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/chat/terminal-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const TerminalPane = memo(function TerminalPane({
>
{entries.length === 0 ? (
<p className="text-static-white/50" data-testid="terminal-log-empty">
{live ? "Booting session…" : "No commands were run."}
{live ? "Starting up…" : "No commands were run."}
</p>
) : (
<>
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/shell/unified-shell-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,12 @@ describe("unified shell contract", () => {
expect(sidebarFrame).toContain("<UserMenu");
});

test("provides a direct Open a surface chooser for the right inspector", () => {
test("provides a direct Open a panel chooser for the right inspector", () => {
const sessionView = readFromFrontend("components/chat/session-view.tsx");
const chooser = readFromFrontend("components/chat/surface-chooser.tsx");

expect(sessionView).toContain("<SurfaceChooser");
expect(chooser).toContain("Open a surface");
expect(chooser).toContain("Open a panel");
for (const label of ["Browser", "Terminal", "Files", "Diff", "Agents"]) {
expect(chooser).toContain(label);
}
Expand Down
Loading