Restore features lost during main merge and fix workspace setup flow

Restores all features dropped when merging main into dev:
- Keyboard shortcuts (send, dictation-confirm) with full e2e chain
- Feature toggles (plan/fast mode) in agent status bar
- Sidebar kebab menu (Remove Project) on project rows
- Combined model selector (sticky header, sizing, favorites sort, search)
- Question form Enter key submit with guard
- Input focus restoration (composer + AgentStatusBar onDropdownClose)
- Draft-agent feature toggles and focus restoration
- Provider diagnostics UI (settings section, diagnostic sheet, status badge)
- Provider diagnostics server (snapshot manager, diagnostic utils, RPC)

Fixes dev-branch regressions:
- New workspace button now opens composer in setup dialog via beginWorkspaceSetup
- Setup tab auto-opens when workspace setup is running
This commit is contained in:
Mohamed Boudra
2026-04-05 21:10:16 +07:00
parent 8e482b1613
commit 4dca672711
26 changed files with 2002 additions and 147 deletions
@@ -42,6 +42,9 @@ import type {
ListProviderModelsResponseMessage,
ListProviderModesResponseMessage,
ListAvailableProvidersResponse,
GetProvidersSnapshotResponseMessage,
RefreshProvidersSnapshotResponseMessage,
ProviderDiagnosticResponseMessage,
ListTerminalsResponse,
CreateTerminalResponse,
SubscribeTerminalResponse,
@@ -152,6 +155,10 @@ export type DaemonEvent =
requestId: string;
resolution: AgentPermissionResponse;
}
| {
type: "providers_snapshot_update";
payload: Extract<SessionOutboundMessage, { type: "providers_snapshot_update" }>["payload"];
}
| { type: "error"; message: string };
export type DaemonEventHandler = (event: DaemonEvent) => void;
@@ -228,6 +235,9 @@ type ListProviderFeaturesPayload = ListProviderFeaturesResponseMessage["payload"
type ListProviderModelsPayload = ListProviderModelsResponseMessage["payload"];
type ListProviderModesPayload = ListProviderModesResponseMessage["payload"];
type ListAvailableProvidersPayload = ListAvailableProvidersResponse["payload"];
type GetProvidersSnapshotPayload = GetProvidersSnapshotResponseMessage["payload"];
type RefreshProvidersSnapshotPayload = RefreshProvidersSnapshotResponseMessage["payload"];
type ProviderDiagnosticPayload = ProviderDiagnosticResponseMessage["payload"];
type ListCommandsPayload = ListCommandsResponse["payload"];
type ListCommandsDraftConfig = Pick<
AgentSessionConfig,
@@ -2574,6 +2584,51 @@ export class DaemonClient {
});
}
async getProvidersSnapshot(options?: {
cwd?: string;
requestId?: string;
}): Promise<GetProvidersSnapshotPayload> {
return this.sendCorrelatedSessionRequest({
requestId: options?.requestId,
message: {
type: "get_providers_snapshot_request",
cwd: options?.cwd,
},
responseType: "get_providers_snapshot_response",
timeout: 10000,
});
}
async refreshProvidersSnapshot(options?: {
cwd?: string;
requestId?: string;
}): Promise<RefreshProvidersSnapshotPayload> {
return this.sendCorrelatedSessionRequest({
requestId: options?.requestId,
message: {
type: "refresh_providers_snapshot_request",
cwd: options?.cwd,
},
responseType: "refresh_providers_snapshot_response",
timeout: 5000,
});
}
async getProviderDiagnostic(
provider: AgentProvider,
options?: { requestId?: string },
): Promise<ProviderDiagnosticPayload> {
return this.sendCorrelatedSessionRequest({
requestId: options?.requestId,
message: {
type: "provider_diagnostic_request",
provider,
},
responseType: "provider_diagnostic_response",
timeout: 30000,
});
}
async listCommands(agentId: string, requestId?: string): Promise<ListCommandsPayload>;
async listCommands(agentId: string, options?: ListCommandsOptions): Promise<ListCommandsPayload>;
async listCommands(
@@ -3668,6 +3723,11 @@ export class DaemonClient {
requestId: msg.payload.requestId,
resolution: msg.payload.resolution,
};
case "providers_snapshot_update":
return {
type: "providers_snapshot_update",
payload: msg.payload,
};
default:
return null;
}
@@ -45,6 +45,8 @@ export type AgentMode = {
description?: string;
};
export type ProviderStatus = "ready" | "loading" | "error" | "unavailable";
export type AgentModelDefinition = {
provider: AgentProvider;
id: string;
@@ -64,6 +66,15 @@ export type AgentSelectOption = {
metadata?: AgentMetadata;
};
export interface ProviderSnapshotEntry {
provider: AgentProvider;
status: ProviderStatus;
error?: string;
models?: AgentModelDefinition[];
modes?: AgentMode[];
fetchedAt?: string;
}
export type AgentFeatureToggle = {
type: "toggle";
id: string;
@@ -469,4 +480,5 @@ export interface AgentClient {
* Returns true if available, false otherwise.
*/
isAvailable(): Promise<boolean>;
getDiagnostic?(): Promise<{ diagnostic: string }>;
}
@@ -0,0 +1,217 @@
import { EventEmitter } from "node:events";
import { resolve } from "node:path";
import type { Logger } from "pino";
import type {
AgentProvider,
ProviderSnapshotEntry,
} from "./agent-sdk-types.js";
import type { ProviderDefinition } from "./provider-registry.js";
import { AGENT_PROVIDER_IDS } from "./provider-manifest.js";
const DEFAULT_CWD_KEY = "__default__";
type ProviderSnapshotChangeListener = (
entries: ProviderSnapshotEntry[],
cwd?: string,
) => void;
export class ProviderSnapshotManager {
private readonly snapshots = new Map<string, Map<AgentProvider, ProviderSnapshotEntry>>();
private readonly warmUps = new Map<string, Promise<void>>();
private readonly events = new EventEmitter();
private destroyed = false;
constructor(
private readonly providerRegistry: Record<AgentProvider, ProviderDefinition>,
private readonly logger: Logger,
) {}
getSnapshot(cwd?: string): ProviderSnapshotEntry[] {
const cwdKey = normalizeCwdKey(cwd);
const entries = this.snapshots.get(cwdKey);
if (!entries) {
const loadingEntries = this.createLoadingEntries();
this.snapshots.set(cwdKey, loadingEntries);
void this.warmUp(cwd);
return entriesToArray(loadingEntries);
}
return entriesToArray(entries);
}
refresh(cwd?: string): void {
const cwdKey = normalizeCwdKey(cwd);
this.snapshots.set(cwdKey, this.createLoadingEntries());
void this.warmUp(cwd);
}
on(event: "change", listener: ProviderSnapshotChangeListener): this {
this.events.on(event, listener);
return this;
}
off(event: "change", listener: ProviderSnapshotChangeListener): this {
this.events.off(event, listener);
return this;
}
destroy(): void {
this.destroyed = true;
this.events.removeAllListeners();
this.snapshots.clear();
this.warmUps.clear();
}
private createLoadingEntries(): Map<AgentProvider, ProviderSnapshotEntry> {
const entries = new Map<AgentProvider, ProviderSnapshotEntry>();
for (const provider of this.getProviderIds()) {
entries.set(provider, {
provider,
status: "loading",
});
}
return entries;
}
private async warmUp(cwd?: string): Promise<void> {
const cwdKey = normalizeCwdKey(cwd);
const inFlight = this.warmUps.get(cwdKey);
if (inFlight) {
return inFlight;
}
const warmUpPromise = Promise.allSettled(
this.getProviderIds().map((provider) => this.refreshProvider(cwdKey, provider, cwd)),
).then(() => undefined);
this.warmUps.set(cwdKey, warmUpPromise);
try {
await warmUpPromise;
} finally {
if (this.warmUps.get(cwdKey) === warmUpPromise) {
this.warmUps.delete(cwdKey);
}
}
}
private async refreshProvider(
cwdKey: string,
provider: AgentProvider,
cwd?: string,
): Promise<void> {
const definition = this.providerRegistry[provider];
if (!definition) {
return;
}
const snapshot = this.getOrCreateSnapshot(cwdKey);
snapshot.set(provider, {
provider,
status: "loading",
});
try {
const client = definition.createClient(this.logger);
const available = await client.isAvailable();
if (!available) {
snapshot.set(provider, {
provider,
status: "unavailable",
});
this.emitChange(cwdKey);
return;
}
const [models, modes] = await Promise.all([
definition.fetchModels({ cwd }),
definition.fetchModes({ cwd }),
]);
snapshot.set(provider, {
provider,
status: "ready",
models,
modes,
fetchedAt: new Date().toISOString(),
});
this.emitChange(cwdKey);
} catch (error) {
snapshot.set(provider, {
provider,
status: "error",
error: toErrorMessage(error),
});
this.logger.warn({ err: error, provider, cwd: cwdKey }, "Failed to refresh provider snapshot");
this.emitChange(cwdKey);
}
}
private emitChange(cwdKey: string): void {
if (this.destroyed) {
return;
}
const snapshot = this.snapshots.get(cwdKey);
if (!snapshot) {
return;
}
this.events.emit("change", entriesToArray(snapshot), denormalizeCwdKey(cwdKey));
}
private getOrCreateSnapshot(cwdKey: string): Map<AgentProvider, ProviderSnapshotEntry> {
const existing = this.snapshots.get(cwdKey);
if (existing) {
return existing;
}
const created = this.createLoadingEntries();
this.snapshots.set(cwdKey, created);
return created;
}
private getProviderIds(): AgentProvider[] {
return AGENT_PROVIDER_IDS.filter((provider) => this.providerRegistry[provider]);
}
}
function normalizeCwdKey(cwd?: string): string {
if (!cwd) {
return DEFAULT_CWD_KEY;
}
const trimmed = cwd.trim();
if (!trimmed) {
return DEFAULT_CWD_KEY;
}
return resolve(trimmed);
}
function denormalizeCwdKey(cwdKey: string): string | undefined {
return cwdKey === DEFAULT_CWD_KEY ? undefined : cwdKey;
}
function entriesToArray(
entries: Map<AgentProvider, ProviderSnapshotEntry>,
): ProviderSnapshotEntry[] {
return Array.from(entries.values(), cloneEntry);
}
function cloneEntry(entry: ProviderSnapshotEntry): ProviderSnapshotEntry {
return {
...entry,
models: entry.models?.map((model) => ({ ...model })),
modes: entry.modes?.map((mode) => ({ ...mode })),
};
}
function toErrorMessage(error: unknown): string {
if (error instanceof Error && error.message) {
return error.message;
}
if (typeof error === "string" && error) {
return error;
}
return "Unknown error";
}
@@ -0,0 +1,80 @@
import { execFileSync } from "node:child_process";
import type { ProviderRuntimeSettings } from "../provider-launch-config.js";
type DiagnosticEntry = {
label: string;
value: string;
};
export function formatProviderDiagnostic(
providerName: string,
entries: DiagnosticEntry[],
): string {
return [providerName, ...entries.map((entry) => ` ${entry.label}: ${entry.value}`)].join("\n");
}
export function formatProviderDiagnosticError(
providerName: string,
error: unknown,
): string {
return formatProviderDiagnostic(providerName, [
{
label: "Error",
value: error instanceof Error ? error.message : String(error),
},
]);
}
export function formatAvailabilityStatus(available: boolean): string {
return available ? "Available" : "Unavailable";
}
export function formatDiagnosticStatus(
available: boolean,
error?: { source: string; cause: unknown },
): string {
if (error) {
return `Error (${error.source} failed: ${toDiagnosticErrorMessage(error.cause)})`;
}
return formatAvailabilityStatus(available);
}
export function toDiagnosticErrorMessage(error: unknown): string {
if (error instanceof Error && error.message) {
return error.message;
}
if (typeof error === "string" && error.trim().length > 0) {
return error;
}
return "Unknown error";
}
export function resolveBinaryVersion(binaryPath: string): string {
try {
return (
execFileSync(binaryPath, ["--version"], {
encoding: "utf8",
timeout: 5_000,
}).trim() || "unknown"
);
} catch {
return "unknown";
}
}
export function formatConfiguredCommand(
defaultArgv: readonly string[],
runtimeSettings?: ProviderRuntimeSettings,
): string {
const command = runtimeSettings?.command;
if (!command || command.mode === "default") {
return `${defaultArgv.join(" ")} (default)`;
}
if (command.mode === "append") {
return [defaultArgv[0], ...(command.args ?? []), ...defaultArgv.slice(1)].join(" ");
}
return command.argv.join(" ");
}
+108
View File
@@ -68,6 +68,7 @@ export type AgentMcpTransportFactory = () => Promise<Transport>;
import { buildProviderRegistry } from "./agent/provider-registry.js";
import type { AgentProviderRuntimeSettingsMap } from "./agent/provider-launch-config.js";
import { AgentManager } from "./agent/agent-manager.js";
import { ProviderSnapshotManager } from "./agent/provider-snapshot-manager.js";
import type {
AgentTimelineCursor,
AgentTimelineFetchDirection,
@@ -96,6 +97,7 @@ import type {
AgentStreamEvent,
AgentProvider,
AgentPersistenceHandle,
ProviderSnapshotEntry,
} from "./agent/agent-sdk-types.js";
import type { StoredAgentRecord } from "./agent/agent-storage.js";
import type { AgentSnapshotStore } from "./agent/agent-snapshot-store.js";
@@ -392,6 +394,7 @@ export type SessionOptions = {
stt: Resolvable<SpeechToTextProvider | null>;
tts: Resolvable<TextToSpeechProvider | null>;
terminalManager: TerminalManager | null;
providerSnapshotManager?: ProviderSnapshotManager;
serviceRouteStore?: ServiceRouteStore;
getDaemonTcpPort?: () => number | null;
resolveServiceStatus?: (hostname: string) => "running" | "stopped" | null;
@@ -568,6 +571,8 @@ export class Session {
} | null = null;
private readonly MOBILE_BACKGROUND_STREAM_GRACE_MS = 60_000;
private readonly terminalManager: TerminalManager | null;
private readonly providerSnapshotManager: ProviderSnapshotManager | null;
private unsubscribeProviderSnapshotEvents: (() => void) | null = null;
private readonly serviceRouteStore: ServiceRouteStore | null;
private readonly getDaemonTcpPort: (() => number | null) | null;
private readonly resolveServiceStatus: ((hostname: string) => "running" | "stopped" | null) | null;
@@ -625,6 +630,7 @@ export class Session {
stt,
tts,
terminalManager,
providerSnapshotManager,
serviceRouteStore,
getDaemonTcpPort,
resolveServiceStatus,
@@ -665,6 +671,7 @@ export class Session {
});
this.createAgentMcpTransport = createAgentMcpTransport;
this.terminalManager = terminalManager;
this.providerSnapshotManager = providerSnapshotManager ?? null;
this.serviceRouteStore = serviceRouteStore ?? null;
this.getDaemonTcpPort = getDaemonTcpPort ?? null;
this.resolveServiceStatus = resolveServiceStatus ?? null;
@@ -673,6 +680,25 @@ export class Session {
this.handleTerminalsChanged(event),
);
}
if (this.providerSnapshotManager) {
const handleProviderSnapshotChange = (entries: ProviderSnapshotEntry[], cwd?: string) => {
const visibleEntries = entries.filter((entry) =>
this.isProviderVisibleToClient(entry.provider),
);
this.emit({
type: "providers_snapshot_update",
payload: {
cwd,
entries: visibleEntries,
generatedAt: new Date().toISOString(),
},
});
};
this.providerSnapshotManager.on("change", handleProviderSnapshotChange);
this.unsubscribeProviderSnapshotEvents = () => {
this.providerSnapshotManager?.off("change", handleProviderSnapshotChange);
};
}
this.voiceAgentMcpStdio = voice?.voiceAgentMcpStdio ?? null;
this.resolveVoiceTurnDetection = toResolver(voice?.turnDetection ?? null);
this.registerVoiceSpeakHandler = voiceBridge?.registerVoiceSpeakHandler;
@@ -1624,6 +1650,18 @@ export class Session {
await this.handleListAvailableProvidersRequest(msg);
break;
case "get_providers_snapshot_request":
await this.handleGetProvidersSnapshotRequest(msg);
break;
case "refresh_providers_snapshot_request":
await this.handleRefreshProvidersSnapshotRequest(msg);
break;
case "provider_diagnostic_request":
await this.handleProviderDiagnosticRequest(msg);
break;
case "clear_agent_attention":
await this.handleClearAgentAttention(msg.agentId);
break;
@@ -3099,6 +3137,72 @@ export class Session {
}
}
private async handleGetProvidersSnapshotRequest(
msg: Extract<SessionInboundMessage, { type: "get_providers_snapshot_request" }>,
): Promise<void> {
const entries = this.providerSnapshotManager
? this.providerSnapshotManager
.getSnapshot(msg.cwd ? expandTilde(msg.cwd) : undefined)
.filter((entry) => this.isProviderVisibleToClient(entry.provider))
: [];
this.emit({
type: "get_providers_snapshot_response",
payload: {
entries,
generatedAt: new Date().toISOString(),
requestId: msg.requestId,
},
});
}
private async handleRefreshProvidersSnapshotRequest(
msg: Extract<SessionInboundMessage, { type: "refresh_providers_snapshot_request" }>,
): Promise<void> {
this.providerSnapshotManager?.refresh(msg.cwd ? expandTilde(msg.cwd) : undefined);
this.emit({
type: "refresh_providers_snapshot_response",
payload: {
acknowledged: true,
requestId: msg.requestId,
},
});
}
private async handleProviderDiagnosticRequest(
msg: Extract<SessionInboundMessage, { type: "provider_diagnostic_request" }>,
): Promise<void> {
try {
const client = this.providerRegistry[msg.provider].createClient(this.sessionLogger);
const diagnostic = client.getDiagnostic
? (await client.getDiagnostic()).diagnostic
: "No diagnostic available for this provider.";
this.emit({
type: "provider_diagnostic_response",
payload: {
provider: msg.provider,
diagnostic,
requestId: msg.requestId,
},
});
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
this.sessionLogger.error(
{ err, provider: msg.provider },
`Failed to get provider diagnostic for ${msg.provider}`,
);
this.emit({
type: "rpc_error",
payload: {
requestId: msg.requestId,
requestType: msg.type,
error: `Failed to get provider diagnostic: ${err.message}`,
code: "provider_diagnostic_failed",
},
});
}
}
private assertSafeGitRef(ref: string, label: string): void {
if (!/^[A-Za-z0-9._/-]+$/.test(ref)) {
throw new Error(`Invalid ${label}: ${ref}`);
@@ -6913,6 +7017,10 @@ export class Session {
this.unsubscribeTerminalsChanged();
this.unsubscribeTerminalsChanged = null;
}
if (this.unsubscribeProviderSnapshotEvents) {
this.unsubscribeProviderSnapshotEvents();
this.unsubscribeProviderSnapshotEvents = null;
}
this.subscribedTerminalDirectories.clear();
for (const unsubscribeExit of this.terminalExitSubscriptions.values()) {
@@ -31,6 +31,8 @@ import { isHostAllowed } from "./allowed-hosts.js";
import { Session, type SessionLifecycleIntent, type SessionRuntimeMetrics } from "./session.js";
import type { AgentProvider } from "./agent/agent-sdk-types.js";
import type { AgentProviderRuntimeSettingsMap } from "./agent/provider-launch-config.js";
import { ProviderSnapshotManager } from "./agent/provider-snapshot-manager.js";
import { buildProviderRegistry } from "./agent/provider-registry.js";
import { PushTokenStore } from "./push/token-store.js";
import { PushService } from "./push/push-service.js";
import type { ServiceRouteStore } from "./service-proxy.js";
@@ -258,6 +260,7 @@ export class VoiceAssistantWebSocketServer {
private readonly voiceSpeakHandlers = new Map<string, VoiceSpeakHandler>();
private readonly voiceCallerContexts = new Map<string, VoiceCallerContext>();
private readonly agentProviderRuntimeSettings: AgentProviderRuntimeSettingsMap | undefined;
private readonly providerSnapshotManager: ProviderSnapshotManager;
private readonly onLifecycleIntent: ((intent: SessionLifecycleIntent) => void) | null;
private serverCapabilities: ServerCapabilities | undefined;
private runtimeWindowStartedAt = Date.now();
@@ -351,6 +354,13 @@ export class VoiceAssistantWebSocketServer {
this.voice = voice ?? null;
this.dictation = dictation ?? null;
this.agentProviderRuntimeSettings = agentProviderRuntimeSettings;
const providerSnapshotLogger = this.logger.child({ module: "provider-snapshot-manager" });
this.providerSnapshotManager = new ProviderSnapshotManager(
buildProviderRegistry(providerSnapshotLogger, {
runtimeSettings: this.agentProviderRuntimeSettings,
}),
providerSnapshotLogger,
);
this.onLifecycleIntent = onLifecycleIntent ?? null;
this.serviceRouteStore = serviceRouteStore ?? null;
this.getDaemonTcpPort = getDaemonTcpPort ?? null;
@@ -519,6 +529,7 @@ export class VoiceAssistantWebSocketServer {
}
await Promise.all(cleanupPromises);
this.providerSnapshotManager.destroy();
this.checkoutDiffManager.dispose();
this.pendingConnections.clear();
this.sessions.clear();
@@ -669,6 +680,7 @@ export class VoiceAssistantWebSocketServer {
stt: () => this.speech?.resolveStt() ?? null,
tts: () => this.speech?.resolveTts() ?? null,
terminalManager: this.terminalManager,
providerSnapshotManager: this.providerSnapshotManager,
serviceRouteStore: this.serviceRouteStore ?? undefined,
getDaemonTcpPort: this.getDaemonTcpPort ?? undefined,
resolveServiceStatus: this.resolveServiceStatus ?? undefined,
+97
View File
@@ -54,6 +54,8 @@ import type {
AgentPermissionRequest,
AgentPermissionResponse,
AgentPersistenceHandle,
ProviderSnapshotEntry,
ProviderStatus,
AgentRuntimeInfo,
AgentTimelineItem,
ToolCallDetail,
@@ -69,6 +71,13 @@ const AgentModeSchema: z.ZodType<AgentMode> = z.object({
description: z.string().optional(),
});
const ProviderStatusSchema: z.ZodType<ProviderStatus> = z.enum([
"ready",
"loading",
"error",
"unavailable",
]);
const AgentSelectOptionSchema = z.object({
id: z.string(),
label: z.string(),
@@ -114,6 +123,15 @@ const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z.object({
defaultThinkingOptionId: z.string().optional(),
});
const ProviderSnapshotEntrySchema: z.ZodType<ProviderSnapshotEntry> = z.object({
provider: AgentProviderSchema,
status: ProviderStatusSchema,
error: z.string().optional(),
models: z.array(AgentModelDefinitionSchema).optional(),
modes: z.array(AgentModeSchema).optional(),
fetchedAt: z.string().optional(),
});
const AgentCapabilityFlagsSchema: z.ZodType<AgentCapabilityFlags> = z.object({
supportsStreaming: z.boolean(),
supportsSessionPersistence: z.boolean(),
@@ -776,6 +794,24 @@ export const ListAvailableProvidersRequestMessageSchema = z.object({
requestId: z.string(),
});
export const GetProvidersSnapshotRequestMessageSchema = z.object({
type: z.literal("get_providers_snapshot_request"),
cwd: z.string().optional(),
requestId: z.string(),
});
export const RefreshProvidersSnapshotRequestMessageSchema = z.object({
type: z.literal("refresh_providers_snapshot_request"),
cwd: z.string().optional(),
requestId: z.string(),
});
export const ProviderDiagnosticRequestMessageSchema = z.object({
type: z.literal("provider_diagnostic_request"),
provider: AgentProviderSchema,
requestId: z.string(),
});
export const ResumeAgentRequestMessageSchema = z.object({
type: z.literal("resume_agent_request"),
handle: AgentPersistenceHandleSchema,
@@ -1289,6 +1325,9 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
ListProviderModesRequestMessageSchema,
ListProviderFeaturesRequestMessageSchema,
ListAvailableProvidersRequestMessageSchema,
GetProvidersSnapshotRequestMessageSchema,
RefreshProvidersSnapshotRequestMessageSchema,
ProviderDiagnosticRequestMessageSchema,
ResumeAgentRequestMessageSchema,
RefreshAgentRequestMessageSchema,
CancelAgentRequestMessageSchema,
@@ -2284,6 +2323,41 @@ export const ListAvailableProvidersResponseSchema = z.object({
}),
});
export const GetProvidersSnapshotResponseMessageSchema = z.object({
type: z.literal("get_providers_snapshot_response"),
payload: z.object({
entries: z.array(ProviderSnapshotEntrySchema),
generatedAt: z.string(),
requestId: z.string(),
}),
});
export const ProvidersSnapshotUpdateMessageSchema = z.object({
type: z.literal("providers_snapshot_update"),
payload: z.object({
cwd: z.string().optional(),
entries: z.array(ProviderSnapshotEntrySchema),
generatedAt: z.string(),
}),
});
export const RefreshProvidersSnapshotResponseMessageSchema = z.object({
type: z.literal("refresh_providers_snapshot_response"),
payload: z.object({
requestId: z.string(),
acknowledged: z.boolean(),
}),
});
export const ProviderDiagnosticResponseMessageSchema = z.object({
type: z.literal("provider_diagnostic_response"),
payload: z.object({
provider: AgentProviderSchema,
diagnostic: z.string(),
requestId: z.string(),
}),
});
const AgentSlashCommandSchema = z.object({
name: z.string(),
description: z.string(),
@@ -2482,6 +2556,10 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
ListProviderModesResponseMessageSchema,
ListProviderFeaturesResponseMessageSchema,
ListAvailableProvidersResponseSchema,
GetProvidersSnapshotResponseMessageSchema,
ProvidersSnapshotUpdateMessageSchema,
RefreshProvidersSnapshotResponseMessageSchema,
ProviderDiagnosticResponseMessageSchema,
ListCommandsResponseSchema,
ListTerminalsResponseSchema,
TerminalsChangedSchema,
@@ -2568,6 +2646,16 @@ export type ListProviderFeaturesResponseMessage = z.infer<
typeof ListProviderFeaturesResponseMessageSchema
>;
export type ListAvailableProvidersResponse = z.infer<typeof ListAvailableProvidersResponseSchema>;
export type GetProvidersSnapshotResponseMessage = z.infer<
typeof GetProvidersSnapshotResponseMessageSchema
>;
export type ProvidersSnapshotUpdateMessage = z.infer<typeof ProvidersSnapshotUpdateMessageSchema>;
export type RefreshProvidersSnapshotResponseMessage = z.infer<
typeof RefreshProvidersSnapshotResponseMessageSchema
>;
export type ProviderDiagnosticResponseMessage = z.infer<
typeof ProviderDiagnosticResponseMessageSchema
>;
export type ChatCreateResponse = z.infer<typeof ChatCreateResponseSchema>;
export type ChatListResponse = z.infer<typeof ChatListResponseSchema>;
export type ChatInspectResponse = z.infer<typeof ChatInspectResponseSchema>;
@@ -2615,6 +2703,15 @@ export type ListProviderFeaturesRequestMessage = z.infer<
export type ListAvailableProvidersRequestMessage = z.infer<
typeof ListAvailableProvidersRequestMessageSchema
>;
export type GetProvidersSnapshotRequestMessage = z.infer<
typeof GetProvidersSnapshotRequestMessageSchema
>;
export type RefreshProvidersSnapshotRequestMessage = z.infer<
typeof RefreshProvidersSnapshotRequestMessageSchema
>;
export type ProviderDiagnosticRequestMessage = z.infer<
typeof ProviderDiagnosticRequestMessageSchema
>;
export type ChatCreateRequest = z.infer<typeof ChatCreateRequestSchema>;
export type ChatListRequest = z.infer<typeof ChatListRequestSchema>;
export type ChatInspectRequest = z.infer<typeof ChatInspectRequestSchema>;