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
18 changes: 16 additions & 2 deletions .github/workflows/deploy-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ on:
branches: [main, master]
paths:
- "apps/site/**"
- "apps/web/**"
- "packages/**"
- "scripts/check-release-publication.mjs"
- "scripts/deploy-site.mjs"
- "scripts/deploy-demo.mjs"
- "scripts/deploy-site-bundle.mjs"
- "scripts/dispatch-site-deployment.mjs"
- "scripts/generate-release-manifest.mjs"
- "scripts/inspect-linux-update.mjs"
Expand Down Expand Up @@ -153,9 +157,18 @@ jobs:
ref: ${{ steps.immutable_source.outputs.source_sha }}
persist-credentials: false

- name: Check out trusted demo source
if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }}
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ steps.source.outputs.trusted_sha }}
path: .trusted-demo-source
persist-credentials: false
- name: Install dependencies
if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }}
run: pnpm install --frozen-lockfile
run: |
pnpm install --frozen-lockfile
pnpm --dir .trusted-demo-source install --frozen-lockfile

- name: Authenticate to AWS with GitHub OIDC
if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }}
Expand All @@ -169,5 +182,6 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
T4_SITE_BUCKET: ${{ vars.T4_SITE_BUCKET }}
T4_IMMUTABLE_SITE_SOURCE: ${{ github.workspace }}
T4_CLOUDFRONT_DISTRIBUTION_ID: ${{ vars.T4_CLOUDFRONT_DISTRIBUTION_ID }}
run: pnpm deploy:site
run: pnpm --dir .trusted-demo-source deploy:site-bundle
16 changes: 15 additions & 1 deletion apps/web/src/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { getShellData, useShellData } from "../state/shell-data.ts";
import { RAIL_OVERLAY_QUERY, useMediaQuery } from "../hooks/useMediaQuery.ts";
import { isEditableTarget, resolveShortcut } from "../keyboard/shortcuts.ts";
import { buildProjectGroups, listVisibleSessionIds } from "../lib/session-tree.ts";
import { useWorkspace, workspaceStore } from "../state/store-instance.ts";
import { rendererPlatform, useWorkspace, workspaceStore } from "../state/store-instance.ts";
import { RAIL_COLLAPSED_WIDTH, RAIL_WIDTH, selectSessionView } from "../state/workspace-store.ts";
import { CommandPalette } from "./CommandPalette.tsx";
import { CollapsedRail, Rail } from "./Rail.tsx";
Expand Down Expand Up @@ -195,6 +195,20 @@ export function AppShell() {
}}
railToggle={railToggle}
/>
{rendererPlatform.demo && (
<div
aria-label="Sample data notice"
className="flex min-h-7 shrink-0 items-center justify-center gap-1.5 border-border/60 border-b bg-primary/8 px-2 text-center text-xs"
>
<span className="font-semibold text-primary">Sample data</span>
<span aria-hidden="true" className="text-muted-foreground">
·
</span>
<span className="truncate text-muted-foreground">
Explore freely. No live hosts, accounts, or files are connected.
</span>
</div>
)}
<div className="flex min-h-0 flex-1">
{!railOverlaid && !focusMode && (
<>
Expand Down
13 changes: 7 additions & 6 deletions apps/web/src/components/SessionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { PaneContent } from "../features/panes/PaneContent.tsx";
import { TerminalDrawer } from "../features/terminal/TerminalDrawer.tsx";
import { FreshnessBadge, SessionMain, SessionOwnershipBadge } from "../features/transcript/SessionMain.tsx";
import { RIGHT_PANE_DOCK_QUERY, useMediaQuery } from "../hooks/useMediaQuery.ts";
import { useWorkspace, workspaceStore } from "../state/store-instance.ts";
import { rendererPlatform, useWorkspace, workspaceStore } from "../state/store-instance.ts";
import { useDesktopRuntimeSnapshot } from "../platform/desktop-runtime.ts";
import { resolveLiveSession } from "../platform/live-workspace.ts";
import { useShellData } from "../state/shell-data.ts";
Expand Down Expand Up @@ -275,12 +275,13 @@ export function SessionScreen({
const runtimeSnapshot = useDesktopRuntimeSnapshot();
const previewAddress =
runtimeSnapshot === null ? null : resolveLiveSession(runtimeSnapshot, session.id);
const previewCount =
previewAddress === null
const previewCount = rendererPlatform.demo
? 1
: previewAddress === null
? 0
: (runtimeSnapshot?.projection.sessions
.get(`${previewAddress.hostId}\u0000${previewAddress.sessionId}`)
?.previews.size ?? 0);
: (runtimeSnapshot?.projection.sessions.get(
`${previewAddress.hostId}\u0000${previewAddress.sessionId}`,
)?.previews.size ?? 0);
const [panePreviewWidth, setPanePreviewWidth] = useState<number | null>(null);

// Transcript scroll ownership lives in TranscriptTimeline (virtualized
Expand Down
49 changes: 35 additions & 14 deletions apps/web/src/features/agent-view/AgentViewScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,23 @@ function AgentCard({
group,
nowMs,
row,
sampleMode,
snapshot,
onCancel,
}: {
readonly group: AgentViewGroup;
readonly nowMs: number;
readonly row: AgentViewRow;
readonly snapshot: DesktopRuntimeSnapshot;
readonly sampleMode: boolean;
readonly snapshot: DesktopRuntimeSnapshot | null;
readonly onCancel: () => void;
}) {
const { node } = row;
const style = AGENT_STATE_STYLES[node.state];
const availability = agentCancelAvailability(snapshot, group.viewId, node);
const availability =
sampleMode || snapshot === null
? { enabled: false, reason: "Sample data is local and cannot stop an agent." }
: agentCancelAvailability(snapshot, group.viewId, node);
const elapsed = node.state === "running" ? formatElapsed(node.startedAt, nowMs) : "";
const contextPercent =
node.contextUsed === null || node.contextLimit === null || node.contextLimit === 0
Expand Down Expand Up @@ -143,28 +148,43 @@ function AgentCard({
);
}

type AgentViewFixtureProps =
| {
readonly fixtureGroups?: never;
readonly fixtureNowMs?: never;
}
| {
readonly fixtureGroups: readonly AgentViewGroup[];
readonly fixtureNowMs: number;
};

interface AgentViewScreenProps {
readonly controller: AgentViewRuntime | null;
readonly snapshot: DesktopRuntimeSnapshot | null;
readonly onBack: () => void;
readonly onOpenSession: (sessionId: string) => void;
}

export function AgentViewScreen({
controller,
fixtureGroups,
fixtureNowMs,
snapshot,
onBack,
onOpenSession,
}: {
readonly controller: AgentViewRuntime | null;
readonly snapshot: DesktopRuntimeSnapshot | null;
readonly onBack: () => void;
readonly onOpenSession: (sessionId: string) => void;
}) {
}: AgentViewScreenProps & AgentViewFixtureProps) {
const groups = useMemo(
() => (snapshot === null ? [] : deriveAgentViewGroups(snapshot)),
[snapshot],
() => (snapshot === null ? (fixtureGroups ?? []) : deriveAgentViewGroups(snapshot)),
[fixtureGroups, snapshot],
);
const sampleMode = snapshot === null && fixtureGroups !== undefined;
const agentCount = groups.reduce((sum, group) => sum + group.agents.length, 0);
const runningCount = groups.reduce(
(sum, group) =>
sum + group.agents.filter(({ node }) => node.state === "running").length,
(sum, group) => sum + group.agents.filter(({ node }) => node.state === "running").length,
0,
);
const nowMs = useNowTick(runningCount > 0);
const liveNowMs = useNowTick(runningCount > 0 && !sampleMode);
const nowMs = sampleMode && fixtureNowMs !== undefined ? fixtureNowMs : liveNowMs;
const [pending, setPending] = useState<PendingCancel | null>(null);
const [sending, setSending] = useState(false);
const [error, setError] = useState<string | null>(null);
Expand Down Expand Up @@ -211,7 +231,7 @@ export function AgentViewScreen({
{announcement}
</p>

{snapshot === null ? (
{snapshot === null && fixtureGroups === undefined ? (
<Empty className="flex-1 border-0">
<EmptyHeader>
<EmptyTitle>Agent View requires the desktop runtime</EmptyTitle>
Expand Down Expand Up @@ -282,6 +302,7 @@ export function AgentViewScreen({
setPending({ group, row });
}}
row={row}
sampleMode={sampleMode}
snapshot={snapshot}
/>
))}
Expand Down
33 changes: 33 additions & 0 deletions apps/web/src/features/agent-view/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { SHELL_FIXTURE } from "../../fixture/data.ts";
import { FIXTURE_EPOCH_MS, fixtureAgentsForSession } from "../panes/fixtures.ts";
import type { AgentViewGroup } from "./model.ts";

const VISIBLE_SESSION_IDS: Readonly<Record<string, true>> = {
"sess-stream": true,
"sess-bundle": true,
};

export const AGENT_VIEW_FIXTURE_NOW_MS = FIXTURE_EPOCH_MS;

/** Global Agent View sample built from the same agents shown in session panes. */
export const AGENT_VIEW_FIXTURE_GROUPS: readonly AgentViewGroup[] = SHELL_FIXTURE.sessions
.filter((session) => VISIBLE_SESSION_IDS[session.id] === true)
.map((session) => ({
viewId: session.id,
session,
projectName:
SHELL_FIXTURE.projects.find((project) => project.id === session.projectId)?.name ??
"Sample project",
agents: fixtureAgentsForSession(session.id).map((node) => ({
node,
task:
node.kind === "main"
? "Coordinate the reconnect investigation and consolidate verified findings."
: node.kind === "batch"
? "Inspect the replay boundary, soak behavior, and documentation in parallel."
: node.path === null
? "Report a bounded finding back to the parent session."
: `Inspect ${node.path} and report evidence.`,
resumable: node.state === "parked",
})),
}));
5 changes: 5 additions & 0 deletions apps/web/src/features/panes/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,11 @@ function agentsForSession(sessionId: string): readonly AgentNode[] {
);
}

/** Shared deterministic agent corpus for fixture-only surfaces such as Agent View. */
export function fixtureAgentsForSession(sessionId: string): readonly AgentNode[] {
return agentsForSession(sessionId);
}

function fixtureController(api: InspectorStoreApi, clock: () => number): InspectorController {
const editedFiles = new Map<string, string>();
return {
Expand Down
Loading
Loading