feat(model-server): frontend modèles locaux — badge de statut & CRUD serveurs (#35) et wizard multi-profils OpenCode (#36)
Sprint « Modeles locaux », couche frontend. #35 : - F35.1 badge de statut de lancement du serveur local (ModelServerLaunchBadge + useAgentsModelServer). - F35.2 feature model-servers : CRUD (ModelServersPanel / useModelServers / gateway modelServer) et ModelServerSelect. #36 : - Liste multi-profils OpenCode dans le wizard de premier lancement, gateway de clonage (clone_opencode_profile_from_seed). Tests verts (exécution réelle) : tsc propre, vitest 608/608. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -13,6 +13,7 @@ import type {
|
||||
Agent,
|
||||
AgentProfile,
|
||||
GatewayError,
|
||||
ModelServerStatus,
|
||||
TerminalSession,
|
||||
} from "@/domain";
|
||||
import type { LiveAgent, OpenTerminalOptions, TerminalHandle } from "@/ports";
|
||||
@ -41,6 +42,17 @@ export interface AgentLimitState {
|
||||
suspected?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An agent-launch failure surfaced to the UI (F35), from `agentLaunchFailed`.
|
||||
* `cause` namespaces the origin (e.g. `"model_server"`); `code`/`message` are
|
||||
* the stable, actionable failure details.
|
||||
*/
|
||||
export interface AgentLaunchFailure {
|
||||
cause: string;
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** What the agents UI needs from this hook. */
|
||||
export interface AgentsViewModel {
|
||||
/** All agents known to the project. */
|
||||
@ -67,6 +79,19 @@ export interface AgentsViewModel {
|
||||
* absent from the map is unlimited.
|
||||
*/
|
||||
limitByAgent: Record<string, AgentLimitState>;
|
||||
/**
|
||||
* Local model-server lifecycle status keyed by `serverId` (F35), folded from
|
||||
* `modelServerStatusChanged`. Correlate an agent to its server through the
|
||||
* profile's `opencode.localModelServerId`. A server absent from the map has no
|
||||
* observed status yet.
|
||||
*/
|
||||
modelServerStatusByServer: Record<string, ModelServerStatus>;
|
||||
/**
|
||||
* Last agent-launch failure keyed by agent id (F35), folded from
|
||||
* `agentLaunchFailed`. Carries an actionable `message`/`code`/`cause`. Cleared
|
||||
* for an agent as soon as it launches successfully (`agentLaunched`).
|
||||
*/
|
||||
launchFailureByAgent: Record<string, AgentLaunchFailure>;
|
||||
/** Last error message, or `null`. */
|
||||
error: string | null;
|
||||
/** Whether a request is in flight. */
|
||||
@ -154,6 +179,12 @@ export function useAgents(projectId: string): AgentsViewModel {
|
||||
const [limitByAgent, setLimitByAgent] = useState<
|
||||
Record<string, AgentLimitState>
|
||||
>({});
|
||||
const [modelServerStatusByServer, setModelServerStatusByServer] = useState<
|
||||
Record<string, ModelServerStatus>
|
||||
>({});
|
||||
const [launchFailureByAgent, setLaunchFailureByAgent] = useState<
|
||||
Record<string, AgentLaunchFailure>
|
||||
>({});
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setBusy(true);
|
||||
@ -208,6 +239,15 @@ export function useAgents(projectId: string): AgentsViewModel {
|
||||
void refresh();
|
||||
void refreshLiveAgents();
|
||||
}
|
||||
// A successful launch supersedes any prior launch failure for that agent
|
||||
// (F35): drop its stale error banner.
|
||||
if (event.type === "agentLaunched") {
|
||||
setLaunchFailureByAgent((prev) => {
|
||||
if (!(event.agentId in prev)) return prev;
|
||||
const { [event.agentId]: _cleared, ...rest } = prev;
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
// Record which door a delegation came through (mcp vs file). Absent
|
||||
// `source` (older backend) leaves the map untouched → no badge.
|
||||
if (
|
||||
@ -269,6 +309,24 @@ export function useAgents(projectId: string): AgentsViewModel {
|
||||
},
|
||||
}));
|
||||
break;
|
||||
// F35 — local model server lifecycle during launch, keyed by server id.
|
||||
case "modelServerStatusChanged":
|
||||
setModelServerStatusByServer((prev) => ({
|
||||
...prev,
|
||||
[event.serverId]: event.status,
|
||||
}));
|
||||
break;
|
||||
// F35 — a launch failed with an actionable cause/message, keyed by agent.
|
||||
case "agentLaunchFailed":
|
||||
setLaunchFailureByAgent((prev) => ({
|
||||
...prev,
|
||||
[event.agentId]: {
|
||||
cause: event.cause,
|
||||
code: event.code,
|
||||
message: event.message,
|
||||
},
|
||||
}));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -475,6 +533,8 @@ export function useAgents(projectId: string): AgentsViewModel {
|
||||
liveAgents,
|
||||
delegationSourceByRequester,
|
||||
limitByAgent,
|
||||
modelServerStatusByServer,
|
||||
launchFailureByAgent,
|
||||
error,
|
||||
busy,
|
||||
runningAgentId,
|
||||
|
||||
Reference in New Issue
Block a user