feat: gate claw runtime selection by availability

This commit is contained in:
kris
2026-04-03 02:11:41 +08:00
parent 6c999fb951
commit 8e2350e89d
19 changed files with 564 additions and 123 deletions

View File

@@ -5,6 +5,7 @@ import {
hasPersistedProject,
updateProjectAgentControls,
} from "@/lib/boss-data";
import { getClawBackendAvailability } from "@/lib/execution/backends/claw-config";
const reasoningEffortValues = new Set(["low", "medium", "high"]);
@@ -27,8 +28,11 @@ export async function GET(
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const controls = await getProjectAgentControls(projectId, session.account);
return NextResponse.json({ ok: true, controls });
const [controls, clawAvailability] = await Promise.all([
getProjectAgentControls(projectId, session.account),
getClawBackendAvailability(),
]);
return NextResponse.json({ ok: true, controls, clawAvailability });
}
export async function POST(
@@ -102,6 +106,16 @@ export async function POST(
}
try {
if (hasBackendOverride && payload.backendOverride === "claw-runtime") {
const clawAvailability = await getClawBackendAvailability();
if (!clawAvailability.selectable) {
return NextResponse.json(
{ ok: false, message: clawAvailability.reasonLabel, clawAvailability },
{ status: 400 },
);
}
}
const controls = await updateProjectAgentControls(
projectId,
{
@@ -112,7 +126,11 @@ export async function POST(
},
session.account,
);
return NextResponse.json({ ok: true, controls: controls ?? null });
return NextResponse.json({
ok: true,
controls: controls ?? null,
clawAvailability: await getClawBackendAvailability(),
});
} catch (error) {
return NextResponse.json(
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },

View File

@@ -9,6 +9,7 @@ import {
updateProjectAgentControls,
updateUserMasterPrompt,
} from "@/lib/boss-data";
import { getClawBackendAvailability } from "@/lib/execution/backends/claw-config";
export async function GET(
request: NextRequest,
@@ -25,10 +26,11 @@ export async function GET(
return NextResponse.json({ ok: false, message: "PROJECT_NOT_FOUND" }, { status: 404 });
}
const [promptPolicy, userPrompt, projectControls] = await Promise.all([
const [promptPolicy, userPrompt, projectControls, clawAvailability] = await Promise.all([
getMasterAgentPromptPolicy(),
getUserMasterPrompt(session.account),
getProjectAgentControls(projectId, session.account),
getClawBackendAvailability(),
]);
return NextResponse.json({
@@ -38,6 +40,7 @@ export async function GET(
userPrompt,
projectControls,
projectPromptOverride: projectControls?.promptOverride ?? null,
clawAvailability,
account: session.account,
});
}
@@ -105,6 +108,24 @@ export async function POST(
}
try {
if (
hasBackendOverride &&
typeof payload.backendOverride === "string" &&
payload.backendOverride.trim() === "claw-runtime"
) {
const clawAvailability = await getClawBackendAvailability();
if (!clawAvailability.selectable) {
return NextResponse.json(
{
ok: false,
message: clawAvailability.reasonLabel,
clawAvailability,
},
{ status: 400 },
);
}
}
if (hasUserPromptContent) {
const userPromptContent = typeof payload.userPromptContent === "string" ? payload.userPromptContent.trim() : "";
if (userPromptContent) {
@@ -121,10 +142,11 @@ export async function POST(
}, session.account);
}
const [promptPolicy, userPrompt, projectControls] = await Promise.all([
const [promptPolicy, userPrompt, projectControls, clawAvailability] = await Promise.all([
getMasterAgentPromptPolicy(),
getUserMasterPrompt(session.account),
getProjectAgentControls(projectId, session.account),
getClawBackendAvailability(),
]);
return NextResponse.json({
@@ -134,6 +156,7 @@ export async function POST(
userPrompt,
projectControls,
projectPromptOverride: projectControls?.promptOverride ?? null,
clawAvailability,
account: session.account,
});
} catch (error) {

View File

@@ -2,6 +2,7 @@ import { AppShell, PageNav, StatusBar } from "@/components/app-ui";
import { MasterAgentPromptMemoryClient } from "@/components/master-agent-prompt-memory-client";
import { requirePageSession } from "@/lib/boss-auth";
import { MASTER_AGENT_CHAT_PAGE_ANCHORS } from "@/lib/master-agent-chat-menu";
import { getClawBackendAvailability } from "@/lib/execution/backends/claw-config";
import {
getMasterAgentPromptPolicy,
getProjectAgentControls,
@@ -13,13 +14,14 @@ export const dynamic = "force-dynamic";
export default async function MasterAgentPromptMemoryPage() {
const session = await requirePageSession();
const [promptPolicy, userPrompt, projectControls, globalMemories, projectMemories] =
const [promptPolicy, userPrompt, projectControls, globalMemories, projectMemories, clawAvailability] =
await Promise.all([
getMasterAgentPromptPolicy(),
getUserMasterPrompt(session.account),
getProjectAgentControls("master-agent", session.account),
listUserMasterMemories(session.account, { includeArchived: false, scope: "global" }),
listUserMasterMemories(session.account, { includeArchived: false, scope: "project" }),
getClawBackendAvailability(),
]);
return (
@@ -42,6 +44,7 @@ export default async function MasterAgentPromptMemoryPage() {
promptPolicy={promptPolicy}
userPrompt={userPrompt}
projectControls={projectControls}
clawAvailability={clawAvailability}
globalMemories={globalMemories}
projectMemories={projectMemories}
anchors={MASTER_AGENT_CHAT_PAGE_ANCHORS}