fix: correct tmux target fallback and deduplicate orchestrator listing

- Fix tmux target to use session ID when runtimeHandle is missing,
  preventing references to non-existent sessions
- Extract mapSessionsToOrchestrators helper to eliminate duplicate
  orchestrator listing logic between page.tsx and API route

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
harshitsinghbhandari 2026-04-04 22:43:11 +05:30 committed by Gaurav Bhola
parent 63a796274a
commit 9c0245a33a
4 changed files with 32 additions and 28 deletions

View File

@ -1023,9 +1023,8 @@ async function runStartup(
);
const selected = sortedOrchestrators[0];
selectedOrchestratorId = selected.id;
if (selected.runtimeHandle?.id) {
tmuxTarget = selected.runtimeHandle.id;
}
// Use runtimeHandle.id if available, otherwise fall back to the session ID
tmuxTarget = selected.runtimeHandle?.id ?? selected.id;
spinner.succeed(
`Using existing orchestrator session: ${selected.id}` +
(existingOrchestrators.length > 1

View File

@ -1,7 +1,8 @@
import { type NextRequest, NextResponse } from "next/server";
import { generateOrchestratorPrompt, isOrchestratorSession } from "@composio/ao-core";
import { generateOrchestratorPrompt } from "@composio/ao-core";
import { getServices } from "@/lib/services";
import { validateIdentifier, validateConfiguredProject } from "@/lib/validation";
import { mapSessionsToOrchestrators } from "@/lib/orchestrator-utils";
/**
* GET /api/orchestrators?project=<projectId>
@ -29,17 +30,7 @@ export async function GET(request: NextRequest) {
const sessionPrefix = project.sessionPrefix ?? projectId;
const allSessions = await sessionManager.list(projectId);
const orchestrators = allSessions
.filter((s) => isOrchestratorSession(s, sessionPrefix))
.map((s) => ({
id: s.id,
projectId: s.projectId,
projectName: project.name,
status: s.status,
activity: s.activity,
createdAt: s.createdAt?.toISOString() ?? null,
lastActivityAt: s.lastActivityAt?.toISOString() ?? null,
}));
const orchestrators = mapSessionsToOrchestrators(allSessions, sessionPrefix, project.name);
return NextResponse.json({ orchestrators, projectName: project.name });
} catch (err) {

View File

@ -1,8 +1,8 @@
import type { Metadata } from "next";
import { OrchestratorSelector, type Orchestrator } from "@/components/OrchestratorSelector";
import { getServices } from "@/lib/services";
import { isOrchestratorSession } from "@composio/ao-core/types";
import { getAllProjects } from "@/lib/project-name";
import { mapSessionsToOrchestrators } from "@/lib/orchestrator-utils";
export const dynamic = "force-dynamic";
@ -57,18 +57,7 @@ export default async function OrchestratorsRoute(props: {
projectName = project.name;
const sessionPrefix = project.sessionPrefix ?? projectId;
const allSessions = await sessionManager.list(projectId);
orchestrators = allSessions
.filter((s) => isOrchestratorSession(s, sessionPrefix))
.map((s) => ({
id: s.id,
projectId: s.projectId,
projectName: project.name,
status: s.status,
activity: s.activity,
createdAt: s.createdAt?.toISOString() ?? null,
lastActivityAt: s.lastActivityAt?.toISOString() ?? null,
}));
orchestrators = mapSessionsToOrchestrators(allSessions, sessionPrefix, project.name);
}
} catch (err) {
error = err instanceof Error ? err.message : "Failed to load orchestrators";

View File

@ -0,0 +1,25 @@
import type { Session } from "@composio/ao-core";
import { isOrchestratorSession } from "@composio/ao-core/types";
import type { Orchestrator } from "@/components/OrchestratorSelector";
/**
* Filter and map sessions to orchestrator DTOs.
* Shared between page.tsx and API route to ensure consistent orchestrator listing.
*/
export function mapSessionsToOrchestrators(
sessions: Session[],
sessionPrefix: string,
projectName: string,
): Orchestrator[] {
return sessions
.filter((s) => isOrchestratorSession(s, sessionPrefix))
.map((s) => ({
id: s.id,
projectId: s.projectId,
projectName,
status: s.status,
activity: s.activity,
createdAt: s.createdAt?.toISOString() ?? null,
lastActivityAt: s.lastActivityAt?.toISOString() ?? null,
}));
}