mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 05:02:11 +00:00
Fix E2B sandbox timeout comment, frontend stability, and create-flow improvements
- Add TEMPORARY comment on E2B timeoutMs with pointer to rivetkit sandbox resilience proposal for when autoPause lands - Fix React useEffect dependency stability in mock-layout and organization-dashboard to prevent infinite re-render loops - Fix terminal-pane ref handling - Improve create-flow service and tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
78cd38d826
commit
ee25a6c6a5
6 changed files with 106 additions and 42 deletions
|
|
@ -205,9 +205,12 @@ const baseTaskSandbox = sandboxActor({
|
||||||
create: () => ({
|
create: () => ({
|
||||||
template: config.sandboxProviders.e2b.template ?? "sandbox-agent-full-0.3.x",
|
template: config.sandboxProviders.e2b.template ?? "sandbox-agent-full-0.3.x",
|
||||||
envs: sandboxEnvObject(),
|
envs: sandboxEnvObject(),
|
||||||
// Default E2B timeout is 5 minutes which is too short for task work.
|
// TEMPORARY: Default E2B timeout is 5 minutes which is too short.
|
||||||
// Set to 1 hour. TODO: use betaCreate + autoPause instead so sandboxes
|
// Set to 1 hour as a stopgap. Remove this once the E2B provider in
|
||||||
// pause (preserving state) rather than being killed on timeout.
|
// sandbox-agent uses betaCreate + autoPause (see
|
||||||
|
// .context/proposal-rivetkit-sandbox-resilience.md). At that point
|
||||||
|
// the provider handles timeout/pause lifecycle and this override is
|
||||||
|
// unnecessary.
|
||||||
timeoutMs: 60 * 60 * 1000,
|
timeoutMs: 60 * 60 * 1000,
|
||||||
}),
|
}),
|
||||||
installAgents: ["claude", "codex"],
|
installAgents: ["claude", "codex"],
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { BRANCH_NAME_PREFIXES } from "./branch-name-prefixes.js";
|
||||||
|
|
||||||
export interface ResolveCreateFlowDecisionInput {
|
export interface ResolveCreateFlowDecisionInput {
|
||||||
task: string;
|
task: string;
|
||||||
explicitTitle?: string;
|
explicitTitle?: string;
|
||||||
|
|
@ -89,30 +91,42 @@ export function sanitizeBranchName(input: string): string {
|
||||||
return trimmed.slice(0, 50).replace(/-+$/g, "");
|
return trimmed.slice(0, 50).replace(/-+$/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateRandomSuffix(length: number): string {
|
||||||
|
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let result = "";
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateBranchName(): string {
|
||||||
|
const prefix = BRANCH_NAME_PREFIXES[Math.floor(Math.random() * BRANCH_NAME_PREFIXES.length)]!;
|
||||||
|
const suffix = generateRandomSuffix(4);
|
||||||
|
return `${prefix}-${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveCreateFlowDecision(input: ResolveCreateFlowDecisionInput): ResolveCreateFlowDecisionResult {
|
export function resolveCreateFlowDecision(input: ResolveCreateFlowDecisionInput): ResolveCreateFlowDecisionResult {
|
||||||
const explicitBranch = input.explicitBranchName?.trim();
|
const explicitBranch = input.explicitBranchName?.trim();
|
||||||
const title = deriveFallbackTitle(input.task, input.explicitTitle);
|
const title = deriveFallbackTitle(input.task, input.explicitTitle);
|
||||||
const generatedBase = sanitizeBranchName(title) || "task";
|
|
||||||
|
|
||||||
const branchBase = explicitBranch && explicitBranch.length > 0 ? explicitBranch : generatedBase;
|
|
||||||
|
|
||||||
const existingBranches = new Set(input.localBranches.map((value) => value.trim()).filter((value) => value.length > 0));
|
const existingBranches = new Set(input.localBranches.map((value) => value.trim()).filter((value) => value.length > 0));
|
||||||
const existingTaskBranches = new Set(input.taskBranches.map((value) => value.trim()).filter((value) => value.length > 0));
|
const existingTaskBranches = new Set(input.taskBranches.map((value) => value.trim()).filter((value) => value.length > 0));
|
||||||
const conflicts = (name: string): boolean => existingBranches.has(name) || existingTaskBranches.has(name);
|
const conflicts = (name: string): boolean => existingBranches.has(name) || existingTaskBranches.has(name);
|
||||||
|
|
||||||
if (explicitBranch && conflicts(branchBase)) {
|
if (explicitBranch && explicitBranch.length > 0) {
|
||||||
throw new Error(`Branch '${branchBase}' already exists. Choose a different --name/--branch value.`);
|
if (conflicts(explicitBranch)) {
|
||||||
|
throw new Error(`Branch '${explicitBranch}' already exists. Choose a different --name/--branch value.`);
|
||||||
|
}
|
||||||
|
return { title, branchName: explicitBranch };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (explicitBranch) {
|
// Generate a random McMaster-Carr-style branch name, retrying on conflicts
|
||||||
return { title, branchName: branchBase };
|
let candidate = generateBranchName();
|
||||||
}
|
let attempts = 0;
|
||||||
|
while (conflicts(candidate) && attempts < 100) {
|
||||||
let candidate = branchBase;
|
candidate = generateBranchName();
|
||||||
let index = 2;
|
attempts += 1;
|
||||||
while (conflicts(candidate)) {
|
|
||||||
candidate = `${branchBase}-${index}`;
|
|
||||||
index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { deriveFallbackTitle, resolveCreateFlowDecision, sanitizeBranchName } from "../src/services/create-flow.js";
|
import { deriveFallbackTitle, resolveCreateFlowDecision, sanitizeBranchName } from "../src/services/create-flow.js";
|
||||||
|
import { BRANCH_NAME_PREFIXES } from "../src/services/branch-name-prefixes.js";
|
||||||
|
|
||||||
describe("create flow decision", () => {
|
describe("create flow decision", () => {
|
||||||
it("derives a conventional-style fallback title from task text", () => {
|
it("derives a conventional-style fallback title from task text", () => {
|
||||||
|
|
@ -17,15 +18,49 @@ describe("create flow decision", () => {
|
||||||
expect(sanitizeBranchName(" spaces everywhere ")).toBe("spaces-everywhere");
|
expect(sanitizeBranchName(" spaces everywhere ")).toBe("spaces-everywhere");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("auto-increments generated branch names for conflicts", () => {
|
it("generates a McMaster-Carr-style branch name with random suffix", () => {
|
||||||
const resolved = resolveCreateFlowDecision({
|
const resolved = resolveCreateFlowDecision({
|
||||||
task: "Add auth",
|
task: "Add auth",
|
||||||
localBranches: ["feat-add-auth"],
|
localBranches: [],
|
||||||
taskBranches: ["feat-add-auth-2"],
|
taskBranches: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(resolved.title).toBe("feat: Add auth");
|
expect(resolved.title).toBe("feat: Add auth");
|
||||||
expect(resolved.branchName).toBe("feat-add-auth-3");
|
// Branch name should be "<prefix>-<4-char-suffix>" where prefix is from BRANCH_NAME_PREFIXES
|
||||||
|
const lastDash = resolved.branchName.lastIndexOf("-");
|
||||||
|
const prefix = resolved.branchName.slice(0, lastDash);
|
||||||
|
const suffix = resolved.branchName.slice(lastDash + 1);
|
||||||
|
expect(BRANCH_NAME_PREFIXES).toContain(prefix);
|
||||||
|
expect(suffix).toMatch(/^[a-z0-9]{4}$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("avoids conflicts by generating a different random name", () => {
|
||||||
|
// Even with a conflicting branch, it should produce something different
|
||||||
|
const resolved = resolveCreateFlowDecision({
|
||||||
|
task: "Add auth",
|
||||||
|
localBranches: [],
|
||||||
|
taskBranches: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Running again with the first result as a conflict should produce a different name
|
||||||
|
const resolved2 = resolveCreateFlowDecision({
|
||||||
|
task: "Add auth",
|
||||||
|
localBranches: [resolved.branchName],
|
||||||
|
taskBranches: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolved2.branchName).not.toBe(resolved.branchName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses explicit branch name when provided", () => {
|
||||||
|
const resolved = resolveCreateFlowDecision({
|
||||||
|
task: "new task",
|
||||||
|
explicitBranchName: "my-branch",
|
||||||
|
localBranches: [],
|
||||||
|
taskBranches: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolved.branchName).toBe("my-branch");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("fails when explicit branch already exists", () => {
|
it("fails when explicit branch already exists", () => {
|
||||||
|
|
|
||||||
|
|
@ -1317,11 +1317,14 @@ export function MockLayout({ organizationId, selectedTaskId, selectedSessionId }
|
||||||
[organizationId],
|
[organizationId],
|
||||||
);
|
);
|
||||||
const organizationState = useSubscription(subscriptionManager, "organization", { organizationId });
|
const organizationState = useSubscription(subscriptionManager, "organization", { organizationId });
|
||||||
const organizationRepos = organizationState.data?.repos ?? [];
|
const organizationReposData = organizationState.data?.repos;
|
||||||
const taskSummaries = organizationState.data?.taskSummaries ?? [];
|
const taskSummariesData = organizationState.data?.taskSummaries;
|
||||||
|
const openPullRequestsData = organizationState.data?.openPullRequests;
|
||||||
|
const organizationRepos = organizationReposData ?? [];
|
||||||
|
const taskSummaries = taskSummariesData ?? [];
|
||||||
const selectedTaskSummary = useMemo(
|
const selectedTaskSummary = useMemo(
|
||||||
() => taskSummaries.find((task) => task.id === selectedTaskId) ?? taskSummaries[0] ?? null,
|
() => taskSummaries.find((task) => task.id === selectedTaskId) ?? taskSummaries[0] ?? null,
|
||||||
[selectedTaskId, taskSummaries],
|
[selectedTaskId, taskSummariesData],
|
||||||
);
|
);
|
||||||
const taskState = useSubscription(
|
const taskState = useSubscription(
|
||||||
subscriptionManager,
|
subscriptionManager,
|
||||||
|
|
@ -1401,9 +1404,9 @@ export function MockLayout({ organizationId, selectedTaskId, selectedSessionId }
|
||||||
summary.id === selectedTaskSummary?.id ? toTaskModel(summary, taskState.data, sessionCache) : toTaskModel(summary),
|
summary.id === selectedTaskSummary?.id ? toTaskModel(summary, taskState.data, sessionCache) : toTaskModel(summary),
|
||||||
);
|
);
|
||||||
return hydratedTasks.sort((left, right) => right.updatedAtMs - left.updatedAtMs);
|
return hydratedTasks.sort((left, right) => right.updatedAtMs - left.updatedAtMs);
|
||||||
}, [selectedTaskSummary, selectedSessionId, sessionState.data, taskState.data, taskSummaries, organizationId]);
|
}, [selectedTaskSummary, selectedSessionId, sessionState.data, taskState.data, taskSummariesData, organizationId]);
|
||||||
const openPullRequests = organizationState.data?.openPullRequests ?? [];
|
const openPullRequests = openPullRequestsData ?? [];
|
||||||
const rawRepositories = useMemo(() => groupRepositories(organizationRepos, tasks, openPullRequests), [tasks, organizationRepos, openPullRequests]);
|
const rawRepositories = useMemo(() => groupRepositories(organizationRepos, tasks, openPullRequests), [tasks, organizationReposData, openPullRequestsData]);
|
||||||
const appSnapshot = useMockAppSnapshot();
|
const appSnapshot = useMockAppSnapshot();
|
||||||
const currentUser = activeMockUser(appSnapshot);
|
const currentUser = activeMockUser(appSnapshot);
|
||||||
const activeOrg = activeMockOrganization(appSnapshot);
|
const activeOrg = activeMockOrganization(appSnapshot);
|
||||||
|
|
@ -1591,6 +1594,7 @@ export function MockLayout({ organizationId, selectedTaskId, selectedSessionId }
|
||||||
}, [activeTask, lastAgentSessionIdByTask, selectedSessionId, syncRouteSession]);
|
}, [activeTask, lastAgentSessionIdByTask, selectedSessionId, syncRouteSession]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const organizationRepos = organizationReposData ?? [];
|
||||||
if (selectedNewTaskRepoId && organizationRepos.some((repo) => repo.id === selectedNewTaskRepoId)) {
|
if (selectedNewTaskRepoId && organizationRepos.some((repo) => repo.id === selectedNewTaskRepoId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1600,7 +1604,7 @@ export function MockLayout({ organizationId, selectedTaskId, selectedSessionId }
|
||||||
if (fallbackRepoId !== selectedNewTaskRepoId) {
|
if (fallbackRepoId !== selectedNewTaskRepoId) {
|
||||||
setSelectedNewTaskRepoId(fallbackRepoId);
|
setSelectedNewTaskRepoId(fallbackRepoId);
|
||||||
}
|
}
|
||||||
}, [activeTask?.repoId, selectedNewTaskRepoId, organizationRepos]);
|
}, [activeTask?.repoId, selectedNewTaskRepoId, organizationReposData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!activeTask) {
|
if (!activeTask) {
|
||||||
|
|
|
||||||
|
|
@ -305,7 +305,8 @@ export function TerminalPane({ organizationId, taskId, isExpanded, onExpand, onC
|
||||||
setProcessTabs([]);
|
setProcessTabs([]);
|
||||||
}, [taskId]);
|
}, [taskId]);
|
||||||
|
|
||||||
const processes = processesState.data ?? [];
|
const processesData = processesState.data;
|
||||||
|
const processes = processesData ?? [];
|
||||||
|
|
||||||
const openTerminalTab = useCallback((process: SandboxProcessRecord) => {
|
const openTerminalTab = useCallback((process: SandboxProcessRecord) => {
|
||||||
setProcessTabs((current) => {
|
setProcessTabs((current) => {
|
||||||
|
|
@ -361,7 +362,7 @@ export function TerminalPane({ organizationId, taskId, isExpanded, onExpand, onC
|
||||||
const activeProcessTab = activeSessionId ? (processTabsById.get(activeSessionId) ?? null) : null;
|
const activeProcessTab = activeSessionId ? (processTabsById.get(activeSessionId) ?? null) : null;
|
||||||
const activeTerminalProcess = useMemo(
|
const activeTerminalProcess = useMemo(
|
||||||
() => (activeProcessTab ? (processes.find((process) => process.id === activeProcessTab.processId) ?? null) : null),
|
() => (activeProcessTab ? (processes.find((process) => process.id === activeProcessTab.processId) ?? null) : null),
|
||||||
[activeProcessTab, processes],
|
[activeProcessTab, processesData],
|
||||||
);
|
);
|
||||||
|
|
||||||
const emptyBodyClassName = css({
|
const emptyBodyClassName = css({
|
||||||
|
|
|
||||||
|
|
@ -335,9 +335,11 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
const activeOrg = appState.data ? currentFoundryOrganization(appState.data) : null;
|
const activeOrg = appState.data ? currentFoundryOrganization(appState.data) : null;
|
||||||
|
|
||||||
const organizationState = useSubscription(subscriptionManager, "organization", { organizationId });
|
const organizationState = useSubscription(subscriptionManager, "organization", { organizationId });
|
||||||
const repos = organizationState.data?.repos ?? [];
|
const reposData = organizationState.data?.repos;
|
||||||
const rows = organizationState.data?.taskSummaries ?? [];
|
const rowsData = organizationState.data?.taskSummaries;
|
||||||
const selectedSummary = useMemo(() => rows.find((row) => row.id === selectedTaskId) ?? rows[0] ?? null, [rows, selectedTaskId]);
|
const repos = reposData ?? [];
|
||||||
|
const rows = rowsData ?? [];
|
||||||
|
const selectedSummary = useMemo(() => rows.find((row) => row.id === selectedTaskId) ?? rows[0] ?? null, [rowsData, selectedTaskId]);
|
||||||
const taskState = useSubscription(
|
const taskState = useSubscription(
|
||||||
subscriptionManager,
|
subscriptionManager,
|
||||||
"task",
|
"task",
|
||||||
|
|
@ -363,6 +365,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const repos = reposData ?? [];
|
||||||
if (repoOverviewMode && selectedRepoId) {
|
if (repoOverviewMode && selectedRepoId) {
|
||||||
setCreateRepoId(selectedRepoId);
|
setCreateRepoId(selectedRepoId);
|
||||||
return;
|
return;
|
||||||
|
|
@ -370,9 +373,11 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
if (!createRepoId && repos.length > 0) {
|
if (!createRepoId && repos.length > 0) {
|
||||||
setCreateRepoId(repos[0]!.id);
|
setCreateRepoId(repos[0]!.id);
|
||||||
}
|
}
|
||||||
}, [createRepoId, repoOverviewMode, repos, selectedRepoId]);
|
}, [createRepoId, repoOverviewMode, reposData, selectedRepoId]);
|
||||||
|
|
||||||
const repoGroups = useMemo(() => {
|
const repoGroups = useMemo(() => {
|
||||||
|
const repos = reposData ?? [];
|
||||||
|
const rows = rowsData ?? [];
|
||||||
const byRepo = new Map<string, typeof rows>();
|
const byRepo = new Map<string, typeof rows>();
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const bucket = byRepo.get(row.repoId);
|
const bucket = byRepo.get(row.repoId);
|
||||||
|
|
@ -400,7 +405,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
}
|
}
|
||||||
return a.repoLabel.localeCompare(b.repoLabel);
|
return a.repoLabel.localeCompare(b.repoLabel);
|
||||||
});
|
});
|
||||||
}, [repos, rows]);
|
}, [reposData, rowsData]);
|
||||||
|
|
||||||
const selectedForSession = repoOverviewMode ? null : (taskState.data ?? null);
|
const selectedForSession = repoOverviewMode ? null : (taskState.data ?? null);
|
||||||
|
|
||||||
|
|
@ -413,6 +418,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
}, [selectedForSession]);
|
}, [selectedForSession]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const rows = rowsData ?? [];
|
||||||
if (!repoOverviewMode && !selectedTaskId && rows.length > 0) {
|
if (!repoOverviewMode && !selectedTaskId && rows.length > 0) {
|
||||||
void navigate({
|
void navigate({
|
||||||
to: "/organizations/$organizationId/tasks/$taskId",
|
to: "/organizations/$organizationId/tasks/$taskId",
|
||||||
|
|
@ -424,14 +430,15 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [navigate, repoOverviewMode, rows, selectedTaskId, organizationId]);
|
}, [navigate, repoOverviewMode, rowsData, selectedTaskId, organizationId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveSessionId(null);
|
setActiveSessionId(null);
|
||||||
setDraft("");
|
setDraft("");
|
||||||
}, [selectedForSession?.id]);
|
}, [selectedForSession?.id]);
|
||||||
|
|
||||||
const sessionRows = selectedForSession?.sessionsSummary ?? [];
|
const sessionRowsData = selectedForSession?.sessionsSummary;
|
||||||
|
const sessionRows = sessionRowsData ?? [];
|
||||||
const taskStatus = selectedForSession?.status ?? null;
|
const taskStatus = selectedForSession?.status ?? null;
|
||||||
const taskStatusState = describeTaskState(taskStatus);
|
const taskStatusState = describeTaskState(taskStatus);
|
||||||
const taskStateSummary = `${taskStatusState.title}. ${taskStatusState.detail}`;
|
const taskStateSummary = `${taskStatusState.title}. ${taskStatusState.detail}`;
|
||||||
|
|
@ -450,7 +457,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
status: session.status,
|
status: session.status,
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
[activeSessionId, selectedForSession?.activeSessionId, sessionRows],
|
[activeSessionId, selectedForSession?.activeSessionId, sessionRowsData],
|
||||||
);
|
);
|
||||||
const resolvedSessionId = sessionSelection.sessionId;
|
const resolvedSessionId = sessionSelection.sessionId;
|
||||||
const staleSessionId = sessionSelection.staleSessionId;
|
const staleSessionId = sessionSelection.staleSessionId;
|
||||||
|
|
@ -466,7 +473,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
const selectedSessionSummary = useMemo(() => sessionRows.find((session) => session.id === resolvedSessionId) ?? null, [resolvedSessionId, sessionRows]);
|
const selectedSessionSummary = useMemo(() => sessionRows.find((session) => session.id === resolvedSessionId) ?? null, [resolvedSessionId, sessionRowsData]);
|
||||||
const isPendingProvision = selectedSessionSummary?.status === "pending_provision";
|
const isPendingProvision = selectedSessionSummary?.status === "pending_provision";
|
||||||
const isPendingSessionCreate = selectedSessionSummary?.status === "pending_session_create";
|
const isPendingSessionCreate = selectedSessionSummary?.status === "pending_session_create";
|
||||||
const isSessionError = selectedSessionSummary?.status === "error";
|
const isSessionError = selectedSessionSummary?.status === "error";
|
||||||
|
|
@ -523,7 +530,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
activeSandboxId: selectedForSession?.id === task.id ? selectedForSession.activeSandboxId : null,
|
activeSandboxId: selectedForSession?.id === task.id ? selectedForSession.activeSandboxId : null,
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
[repos, rows, selectedForSession, organizationId],
|
[reposData, rowsData, selectedForSession, organizationId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const startSessionFromTask = async (): Promise<{ id: string; status: "running" | "idle" | "error" }> => {
|
const startSessionFromTask = async (): Promise<{ id: string; status: "running" | "idle" | "error" }> => {
|
||||||
|
|
@ -631,7 +638,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
setCreateTaskOpen(true);
|
setCreateTaskOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const repoOptions = useMemo(() => repos.map((repo) => createOption({ id: repo.id, label: repo.label })), [repos]);
|
const repoOptions = useMemo(() => repos.map((repo) => createOption({ id: repo.id, label: repo.label })), [reposData]);
|
||||||
const selectedRepoOption = repoOptions.find((option) => option.id === createRepoId) ?? null;
|
const selectedRepoOption = repoOptions.find((option) => option.id === createRepoId) ?? null;
|
||||||
const selectedFilterOption = useMemo(
|
const selectedFilterOption = useMemo(
|
||||||
() => createOption(FILTER_OPTIONS.find((option) => option.id === overviewFilter) ?? FILTER_OPTIONS[0]!),
|
() => createOption(FILTER_OPTIONS.find((option) => option.id === overviewFilter) ?? FILTER_OPTIONS[0]!),
|
||||||
|
|
@ -639,7 +646,7 @@ export function OrganizationDashboard({ organizationId, selectedTaskId, selected
|
||||||
);
|
);
|
||||||
const sessionOptions = useMemo(
|
const sessionOptions = useMemo(
|
||||||
() => sessionRows.map((session) => createOption({ id: session.id, label: `${session.sessionName} (${session.status})` })),
|
() => sessionRows.map((session) => createOption({ id: session.id, label: `${session.sessionName} (${session.status})` })),
|
||||||
[sessionRows],
|
[sessionRowsData],
|
||||||
);
|
);
|
||||||
const selectedSessionOption = sessionOptions.find((option) => option.id === resolvedSessionId) ?? null;
|
const selectedSessionOption = sessionOptions.find((option) => option.id === resolvedSessionId) ?? null;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue