feat(ux): consolidate Launch Swarm + telemetry UX with minimized strip
- Removed broken LaunchSwarmDialog (formula-based) from TopBar/LeftPanel - All Rocket buttons (TopBar, LeftPanel, DAG nodes, social cards) now open AssignmentPanel (archetype-based) which actually works - Every Rocket clears taskId first so assignMode && !taskId condition passes - Conversation button priority: taskId always shows conversation, not assign panel - Added TelemetryStrip: minimized right sidebar with status dots when non-telemetry panel (conversation/assignment) is active - Live feed has minimize button → restores last taskId or assignMode - DAG nodes: Signal icon → restores telemetry feed - Social button on DAG nodes: single router.push to avoid race (setView + setTaskId) - Fixed social card message button: opens right panel with drawer:closed (no popup) Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
parent
65d69ecbbc
commit
c246ceaf21
165 changed files with 13730 additions and 1132 deletions
37
src/app/api/bd/health/route.ts
Normal file
37
src/app/api/bd/health/route.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { runBdCommand } from '../../../../lib/bridge';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function GET(request: Request): Promise<Response> {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const projectRoot = searchParams.get('projectRoot') ?? process.cwd();
|
||||
|
||||
const result = await runBdCommand({
|
||||
projectRoot,
|
||||
args: ['--version'],
|
||||
timeoutMs: 8_000,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
const status = result.classification === 'not_found' ? 503 : 500;
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
classification: result.classification ?? 'unknown',
|
||||
message: result.error ?? result.stderr ?? 'bd health check failed',
|
||||
},
|
||||
},
|
||||
{ status },
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
data: {
|
||||
version: result.stdout,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ export async function handleMutationRequest(request: Request, operation: Mutatio
|
|||
const payload = validateMutationPayload(operation, body);
|
||||
const result = await executeMutation(operation, payload);
|
||||
|
||||
const status = result.ok ? 200 : result.error?.classification === 'not_found' ? 404 : 400;
|
||||
const status = result.ok ? 200 : result.error?.classification === 'not_found' ? 503 : 400;
|
||||
return NextResponse.json(result, { status });
|
||||
} catch (error) {
|
||||
if (error instanceof MutationValidationError) {
|
||||
|
|
|
|||
56
src/app/api/coord/events/route.ts
Normal file
56
src/app/api/coord/events/route.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { writeCoordEvent } from '../../../../lib/coord-events';
|
||||
|
||||
interface CoordEventsDeps {
|
||||
writeCoordEvent: typeof writeCoordEvent;
|
||||
}
|
||||
|
||||
function parseBody(data: unknown): { projectRoot: string; event: unknown } | null {
|
||||
if (!data || typeof data !== 'object') return null;
|
||||
const record = data as Record<string, unknown>;
|
||||
if (typeof record.projectRoot !== 'string' || !record.projectRoot.trim()) return null;
|
||||
return {
|
||||
projectRoot: record.projectRoot.trim(),
|
||||
event: record.event,
|
||||
};
|
||||
}
|
||||
|
||||
export async function handleCoordEventsPost(
|
||||
request: Request,
|
||||
deps?: Partial<CoordEventsDeps>,
|
||||
): Promise<Response> {
|
||||
let body: unknown;
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: { classification: 'bad_args', message: 'Invalid JSON body' } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const parsed = parseBody(body);
|
||||
if (!parsed) {
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: { classification: 'bad_args', message: 'projectRoot and event are required' } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const writer = deps?.writeCoordEvent ?? writeCoordEvent;
|
||||
const result = await writer(parsed.event, { projectRoot: parsed.projectRoot });
|
||||
if (!result.ok) {
|
||||
const status = result.error.classification === 'bad_args' ? 400 : 500;
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: { classification: result.error.classification, message: result.error.message } },
|
||||
{ status },
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({ ok: true, eventId: result.eventId });
|
||||
}
|
||||
|
||||
export async function POST(request: Request): Promise<Response> {
|
||||
return handleCoordEventsPost(request);
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ export async function GET(
|
|||
const activity = history.filter((e: ActivityEvent) => e.beadId === beadId);
|
||||
|
||||
// 2. Get communication for this bead
|
||||
const summary = await getCommunicationSummary();
|
||||
const summary = await getCommunicationSummary(projectRoot);
|
||||
const messages = summary.messages.filter((m: AgentMessage) => m.bead_id === beadId);
|
||||
|
||||
// 3. Get local bd interactions via CLI
|
||||
|
|
@ -55,4 +55,4 @@ export async function GET(
|
|||
console.error('[API/Sessions/Conversation] Failed:', error);
|
||||
return NextResponse.json({ ok: false, error: String(error) }, { status: 500 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ export async function GET(request: Request): Promise<Response> {
|
|||
try {
|
||||
const issues = await readIssuesFromDisk({ projectRoot, preferBd: true });
|
||||
const activity = activityEventBus.getHistory(projectRoot);
|
||||
const communication = await getCommunicationSummary();
|
||||
const communication = await getCommunicationSummary(projectRoot);
|
||||
const livenessMap = await getAgentLivenessMap(projectRoot, activity);
|
||||
const incursions = await calculateIncursions();
|
||||
const incursions = await calculateIncursions(projectRoot, livenessMap);
|
||||
const agentsResult = await listAgents({}, { projectRoot });
|
||||
|
||||
const feed = buildSessionTaskFeed(issues, activity, communication, livenessMap);
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ export async function GET(request: Request): Promise<Response> {
|
|||
});
|
||||
|
||||
if (!result.success) {
|
||||
const status = result.classification === 'not_found' ? 503 : 400;
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: { classification: result.classification ?? 'unknown', message: result.error ?? result.stderr } },
|
||||
{ status: 400 },
|
||||
{ status },
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,10 @@ export async function GET(request: Request): Promise<Response> {
|
|||
});
|
||||
|
||||
if (!result.success) {
|
||||
const status = result.classification === 'not_found' ? 503 : 400;
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: { classification: result.classification ?? 'unknown', message: result.error ?? result.stderr } },
|
||||
{ status: 400 },
|
||||
{ status },
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue