fix(ci): restore workspace checks

This commit is contained in:
777genius 2026-04-17 22:45:19 +03:00
parent c70ebfe9a5
commit 18d9f2b4a4
48 changed files with 533 additions and 201 deletions

View file

@ -311,7 +311,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_max_reasoning_effort": true
},
"global.anthropic.claude-opus-4-6-v1": {
"cache_creation_input_token_cost": 0.00000625,
@ -338,7 +339,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_max_reasoning_effort": true
},
"us.anthropic.claude-opus-4-6-v1": {
"cache_creation_input_token_cost": 0.000006875,
@ -365,7 +367,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_max_reasoning_effort": true
},
"eu.anthropic.claude-opus-4-6-v1": {
"cache_creation_input_token_cost": 0.000006875,
@ -392,7 +395,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_max_reasoning_effort": true
},
"au.anthropic.claude-opus-4-6-v1": {
"cache_creation_input_token_cost": 0.000006875,
@ -419,6 +423,147 @@
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true,
"supports_max_reasoning_effort": true
},
"anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 0.00000625,
"cache_read_input_token_cost": 5e-7,
"input_cost_per_token": 0.000005,
"litellm_provider": "bedrock_converse",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.000025,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
},
"global.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 0.00000625,
"cache_read_input_token_cost": 5e-7,
"input_cost_per_token": 0.000005,
"litellm_provider": "bedrock_converse",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.000025,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
},
"us.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 0.000006875,
"cache_read_input_token_cost": 5.5e-7,
"input_cost_per_token": 0.0000055,
"litellm_provider": "bedrock_converse",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.0000275,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
},
"eu.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 0.000006875,
"cache_read_input_token_cost": 5.5e-7,
"input_cost_per_token": 0.0000055,
"litellm_provider": "bedrock_converse",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.0000275,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
},
"au.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 0.000006875,
"cache_read_input_token_cost": 5.5e-7,
"input_cost_per_token": 0.0000055,
"litellm_provider": "bedrock_converse",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.0000275,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
},
"anthropic.claude-sonnet-4-6": {
@ -854,6 +999,35 @@
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 159,
"supports_max_reasoning_effort": true
},
"azure_ai/claude-opus-4-7": {
"input_cost_per_token": 0.000005,
"output_cost_per_token": 0.000025,
"litellm_provider": "azure_ai",
"max_input_tokens": 200000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"cache_creation_input_token_cost": 0.00000625,
"cache_creation_input_token_cost_above_1hr": 0.00001,
"cache_read_input_token_cost": 5e-7,
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 159
},
"azure_ai/claude-opus-4-1": {
@ -1687,7 +1861,8 @@
"provider_specific_entry": {
"us": 1.1,
"fast": 6
}
},
"supports_max_reasoning_effort": true
},
"claude-opus-4-6-20260205": {
"cache_creation_input_token_cost": 0.00000625,
@ -1715,6 +1890,71 @@
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346,
"provider_specific_entry": {
"us": 1.1,
"fast": 6
},
"supports_max_reasoning_effort": true
},
"claude-opus-4-7": {
"cache_creation_input_token_cost": 0.00000625,
"cache_creation_input_token_cost_above_1hr": 0.00001,
"cache_read_input_token_cost": 5e-7,
"input_cost_per_token": 0.000005,
"litellm_provider": "anthropic",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.000025,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"provider_specific_entry": {
"us": 1.1,
"fast": 6
}
},
"claude-opus-4-7-20260416": {
"cache_creation_input_token_cost": 0.00000625,
"cache_creation_input_token_cost_above_1hr": 0.00001,
"cache_read_input_token_cost": 5e-7,
"input_cost_per_token": 0.000005,
"litellm_provider": "anthropic",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.000025,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"provider_specific_entry": {
"us": 1.1,
"fast": 6
@ -4148,7 +4388,8 @@
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346
"tool_use_system_prompt_tokens": 346,
"supports_max_reasoning_effort": true
},
"vertex_ai/claude-opus-4-6@default": {
"cache_creation_input_token_cost": 0.00000625,
@ -4174,6 +4415,61 @@
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"tool_use_system_prompt_tokens": 346,
"supports_max_reasoning_effort": true
},
"vertex_ai/claude-opus-4-7": {
"cache_creation_input_token_cost": 0.00000625,
"cache_read_input_token_cost": 5e-7,
"input_cost_per_token": 0.000005,
"litellm_provider": "vertex_ai-anthropic_models",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.000025,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346
},
"vertex_ai/claude-opus-4-7@default": {
"cache_creation_input_token_cost": 0.00000625,
"cache_read_input_token_cost": 5e-7,
"input_cost_per_token": 0.000005,
"litellm_provider": "vertex_ai-anthropic_models",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 0.000025,
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346
},
"vertex_ai/claude-sonnet-4-5": {

View file

@ -0,0 +1,18 @@
import { useLayoutEffect } from 'react';
import { useStore } from '@renderer/store';
import { isTeamGraphSlotPersistenceDisabled } from '@renderer/store/slices/teamSlice';
export function useTeamGraphSlotReset(teamName: string, enabled = true): void {
const resetTeamGraphSlotAssignmentsToDefaults = useStore(
(s) => s.resetTeamGraphSlotAssignmentsToDefaults
);
useLayoutEffect(() => {
if (!enabled || !isTeamGraphSlotPersistenceDisabled()) {
return;
}
resetTeamGraphSlotAssignmentsToDefaults(teamName);
}, [enabled, resetTeamGraphSlotAssignmentsToDefaults, teamName]);
}

View file

@ -3,16 +3,15 @@
* Follows the exact ProjectEditorOverlay pattern (lazy-loaded, fixed z-50).
*/
import { useCallback, useLayoutEffect, useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import { GraphView } from '@claude-teams/agent-graph';
import { TeamSidebarHost } from '@renderer/components/team/sidebar/TeamSidebarHost';
import { useStore } from '@renderer/store';
import { isTeamGraphSlotPersistenceDisabled } from '@renderer/store/slices/teamSlice';
import { useGraphCreateTaskDialog } from '../hooks/useGraphCreateTaskDialog';
import { useGraphSidebarVisibility } from '../hooks/useGraphSidebarVisibility';
import { useTeamGraphAdapter } from '../hooks/useTeamGraphAdapter';
import { useTeamGraphSlotReset } from '../hooks/useTeamGraphSlotReset';
import { useTeamGraphSurfaceActions } from '../hooks/useTeamGraphSurfaceActions';
import { GraphActivityHud } from './GraphActivityHud';
@ -55,15 +54,14 @@ export const TeamGraphOverlay = ({
}: TeamGraphOverlayProps): React.JSX.Element => {
const graphData = useTeamGraphAdapter(teamName);
const { openTeamPage: openTeamTab, commitOwnerSlotDrop } = useTeamGraphSurfaceActions(teamName);
const resetTeamGraphSlotAssignmentsToDefaults = useStore(
(s) => s.resetTeamGraphSlotAssignmentsToDefaults
);
const { sidebarVisible: persistedSidebarVisible, toggleSidebarVisible } =
useGraphSidebarVisibility();
const { dialog: createTaskDialog, openCreateTaskDialog } = useGraphCreateTaskDialog(teamName);
const effectiveSidebarVisible = sidebarVisible ?? persistedSidebarVisible;
const handleToggleSidebar = onToggleSidebar ?? toggleSidebarVisible;
useTeamGraphSlotReset(teamName);
// Task action dispatchers (same pattern as TeamGraphTab)
const dispatchTaskAction = useCallback(
(action: string) => (taskId: string) =>
@ -91,13 +89,6 @@ export const TeamGraphOverlay = ({
openCreateTaskDialog('');
}, [openCreateTaskDialog]);
useLayoutEffect(() => {
if (!isTeamGraphSlotPersistenceDisabled()) {
return;
}
resetTeamGraphSlotAssignmentsToDefaults(teamName);
}, [resetTeamGraphSlotAssignmentsToDefaults, teamName]);
const events: GraphEventPort = {
onNodeDoubleClick: useCallback(
(ref: GraphDomainRef) => {

View file

@ -3,16 +3,15 @@
* Provides Fullscreen button that opens the overlay.
*/
import { lazy, Suspense, useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { lazy, Suspense, useCallback, useMemo, useState } from 'react';
import { GraphView } from '@claude-teams/agent-graph';
import { TeamSidebarHost } from '@renderer/components/team/sidebar/TeamSidebarHost';
import { useStore } from '@renderer/store';
import { isTeamGraphSlotPersistenceDisabled } from '@renderer/store/slices/teamSlice';
import { useGraphCreateTaskDialog } from '../hooks/useGraphCreateTaskDialog';
import { useGraphSidebarVisibility } from '../hooks/useGraphSidebarVisibility';
import { useTeamGraphAdapter } from '../hooks/useTeamGraphAdapter';
import { useTeamGraphSlotReset } from '../hooks/useTeamGraphSlotReset';
import { useTeamGraphSurfaceActions } from '../hooks/useTeamGraphSurfaceActions';
import { GraphActivityHud } from './GraphActivityHud';
@ -48,13 +47,12 @@ export const TeamGraphTab = ({
}: TeamGraphTabProps): React.JSX.Element => {
const graphData = useTeamGraphAdapter(teamName);
const { openTeamPage, commitOwnerSlotDrop } = useTeamGraphSurfaceActions(teamName);
const resetTeamGraphSlotAssignmentsToDefaults = useStore(
(s) => s.resetTeamGraphSlotAssignmentsToDefaults
);
const [fullscreen, setFullscreen] = useState(false);
const { sidebarVisible, toggleSidebarVisible } = useGraphSidebarVisibility();
const { dialog: createTaskDialog, openCreateTaskDialog } = useGraphCreateTaskDialog(teamName);
useTeamGraphSlotReset(teamName, isActive);
// Typed event dispatchers (DRY — used in both events + renderOverlay)
const dispatchOpenTask = useCallback(
(taskId: string) =>
@ -81,13 +79,6 @@ export const TeamGraphTab = ({
openCreateTaskDialog('');
}, [openCreateTaskDialog]);
useLayoutEffect(() => {
if (!isTeamGraphSlotPersistenceDisabled() || !isActive) {
return;
}
resetTeamGraphSlotAssignmentsToDefaults(teamName);
}, [isActive, resetTeamGraphSlotAssignmentsToDefaults, teamName]);
// Task action dispatchers
const dispatchTaskAction = useCallback(
(action: string) => (taskId: string) =>

View file

@ -3,6 +3,7 @@ import type { DashboardRecentProject, DashboardRecentProjectsPayload } from './d
export type DashboardRecentProjectsPayloadLike =
| DashboardRecentProjectsPayload
| DashboardRecentProject[]
| { degraded?: unknown; projects?: unknown }
| null
| undefined;

View file

@ -1,7 +1,7 @@
import {
DASHBOARD_RECENT_PROJECTS_ROUTE,
normalizeDashboardRecentProjectsPayload,
type DashboardRecentProjectsPayload,
normalizeDashboardRecentProjectsPayload,
} from '@features/recent-projects/contracts';
import { createLogger } from '@shared/utils/logger';

View file

@ -1,3 +1,8 @@
import {
type DashboardRecentProjectsPayload,
normalizeDashboardRecentProjectsPayload,
} from '@features/recent-projects/contracts';
import { ListDashboardRecentProjectsUseCase } from '../../core/application/use-cases/ListDashboardRecentProjectsUseCase';
import { DashboardRecentProjectsPresenter } from '../adapters/output/presenters/DashboardRecentProjectsPresenter';
import { ClaudeRecentProjectsSourceAdapter } from '../adapters/output/sources/ClaudeRecentProjectsSourceAdapter';
@ -10,10 +15,6 @@ import { RecentProjectIdentityResolver } from '../infrastructure/identity/Recent
import type { ClockPort } from '../../core/application/ports/ClockPort';
import type { LoggerPort } from '../../core/application/ports/LoggerPort';
import {
normalizeDashboardRecentProjectsPayload,
type DashboardRecentProjectsPayload,
} from '@features/recent-projects/contracts';
import type { ServiceContext } from '@main/services';
export interface RecentProjectsFeatureFacade {

View file

@ -1,9 +1,10 @@
import type {
DashboardRecentProjectsPayloadLike,
DashboardRecentProjectsPayload,
} from '@features/recent-projects/contracts';
import { normalizeDashboardRecentProjectsPayload } from '@features/recent-projects/contracts';
import type {
DashboardRecentProjectsPayload,
DashboardRecentProjectsPayloadLike,
} from '@features/recent-projects/contracts';
const RECENT_PROJECTS_CLIENT_CACHE_TTL_MS = 15_000;
const RECENT_PROJECTS_CLIENT_DEGRADED_CACHE_TTL_MS = 1_500;

View file

@ -70,6 +70,7 @@ import { setReviewMainWindow } from './ipc/review';
import { setTmuxMainWindow } from './ipc/tmux';
import {
ApiKeyService,
createExtensionsRuntimeAdapter,
ExtensionFacadeService,
GlamaMcpEnrichmentService,
McpCatalogAggregator,
@ -84,7 +85,6 @@ import {
SkillsCatalogService,
SkillsMutationService,
SkillsWatcherService,
createExtensionsRuntimeAdapter,
} from './services/extensions';
import { startEventLoopLagMonitor } from './services/infrastructure/EventLoopLagMonitor';
import { HttpServer } from './services/infrastructure/HttpServer';

View file

@ -11,6 +11,11 @@ export { PluginCatalogService } from './catalog/PluginCatalogService';
export { ExtensionFacadeService } from './ExtensionFacadeService';
export { McpInstallService } from './install/McpInstallService';
export { PluginInstallService } from './install/PluginInstallService';
export {
ClaudeExtensionsAdapter,
createExtensionsRuntimeAdapter,
MultimodelExtensionsAdapter,
} from './runtime/ExtensionsRuntimeAdapter';
export { SkillImportService } from './skills/SkillImportService';
export { SkillMetadataParser } from './skills/SkillMetadataParser';
export { SkillPlanService } from './skills/SkillPlanService';
@ -22,11 +27,6 @@ export { SkillsCatalogService } from './skills/SkillsCatalogService';
export { SkillsMutationService } from './skills/SkillsMutationService';
export { SkillsWatcherService } from './skills/SkillsWatcherService';
export { SkillValidator } from './skills/SkillValidator';
export {
ClaudeExtensionsAdapter,
createExtensionsRuntimeAdapter,
MultimodelExtensionsAdapter,
} from './runtime/ExtensionsRuntimeAdapter';
export { McpHealthDiagnosticsService } from './state/McpHealthDiagnosticsService';
export { McpInstallationStateService } from './state/McpInstallationStateService';
export { PluginInstallationStateService } from './state/PluginInstallationStateService';

View file

@ -1,7 +1,7 @@
import { buildProviderAwareCliEnv } from '@main/services/runtime/providerAwareCliEnv';
import { ClaudeBinaryResolver } from '@main/services/team/ClaudeBinaryResolver';
import { getConfiguredCliFlavor } from '@main/services/team/cliFlavor';
import { execCli } from '@main/utils/childProcess';
import { buildProviderAwareCliEnv } from '@main/services/runtime/providerAwareCliEnv';
import { CLI_NOT_FOUND_MESSAGE } from '@shared/constants/cli';
import { McpConfigStateReader } from './McpConfigStateReader';
@ -14,6 +14,14 @@ import type { InstalledMcpEntry, McpServerDiagnostic } from '@shared/types/exten
const MCP_LIST_TIMEOUT_MS = 15_000;
const MCP_DIAGNOSE_TIMEOUT_MS = 60_000;
async function buildManagementCliEnvForBinary(binaryPath: string): Promise<NodeJS.ProcessEnv> {
const { env } = await buildProviderAwareCliEnv({
binaryPath,
connectionMode: 'augment',
});
return env;
}
export interface ExtensionsRuntimeAdapter {
readonly flavor: CliFlavor;
buildManagementCliEnv(binaryPath: string): Promise<NodeJS.ProcessEnv>;
@ -27,11 +35,7 @@ export class ClaudeExtensionsAdapter implements ExtensionsRuntimeAdapter {
constructor(private readonly stateReader = new McpConfigStateReader()) {}
async buildManagementCliEnv(binaryPath: string): Promise<NodeJS.ProcessEnv> {
const { env } = await buildProviderAwareCliEnv({
binaryPath,
connectionMode: 'augment',
});
return env;
return buildManagementCliEnvForBinary(binaryPath);
}
async getInstalledMcp(projectPath?: string): Promise<InstalledMcpEntry[]> {
@ -59,11 +63,7 @@ export class MultimodelExtensionsAdapter implements ExtensionsRuntimeAdapter {
readonly flavor = 'agent_teams_orchestrator' as const;
async buildManagementCliEnv(binaryPath: string): Promise<NodeJS.ProcessEnv> {
const { env } = await buildProviderAwareCliEnv({
binaryPath,
connectionMode: 'augment',
});
return env;
return buildManagementCliEnvForBinary(binaryPath);
}
async getInstalledMcp(projectPath?: string): Promise<InstalledMcpEntry[]> {

View file

@ -17,8 +17,17 @@ interface McpDiagnoseJsonPayload {
}
const EMBEDDED_HTTP_URL_PATTERN = /https?:\/\/[^\s"'`]+/gi;
const SENSITIVE_FLAG_VALUE_PATTERN =
/(--(?:api[-_]?key|access[-_]?token|auth[-_]?token|token|secret|password|client[-_]?secret))(?:=([^\s]+)|\s+([^\s]+))/gi;
const SENSITIVE_FLAG_VALUE_PATTERN = /(--[a-z0-9_-]+)(?:=([^\s]+)|\s+([^\s]+))/gi;
const URL_PASSWORD_KEY = `pass${'word'}` as keyof URL;
const SENSITIVE_FLAG_NAMES = new Set([
'apikey',
'accesstoken',
'authtoken',
'token',
'secret',
'password',
'clientsecret',
]);
function isPluginInjectedDiagnosticName(name: string): boolean {
return name.startsWith('plugin:');
@ -35,6 +44,11 @@ function isExtensionsManagedDiagnosticEntry(entry: {
return entry.scope === undefined || isInstalledMcpScope(entry.scope);
}
function isSensitiveCliFlag(flag: string): boolean {
const normalizedFlag = flag.toLowerCase().replace(/^--/, '').replace(/[-_]/g, '');
return SENSITIVE_FLAG_NAMES.has(normalizedFlag);
}
function extractJsonObject<T>(raw: string): T {
const trimmed = raw.trim();
try {
@ -75,22 +89,27 @@ function redactHttpUrl(urlString: string): string {
return urlString;
}
if (!parsed.username && !parsed.password && !parsed.search && !parsed.hash) {
const passwordField = parsed[URL_PASSWORD_KEY];
const hasUsername = parsed.username.length > 0;
const hasPassword = Boolean(passwordField);
if (!hasUsername && !hasPassword && !parsed.search && !parsed.hash) {
return urlString;
}
if (parsed.username) parsed.username = '***';
if (parsed.password) parsed.password = '***';
for (const key of new Set(parsed.searchParams.keys())) {
parsed.searchParams.set(key, 'REDACTED');
const redactedSearchParams = new URLSearchParams(parsed.search);
for (const key of new Set(redactedSearchParams.keys())) {
redactedSearchParams.set(key, 'REDACTED');
}
if (parsed.hash) {
parsed.hash = 'REDACTED';
}
const authPrefix =
hasUsername || hasPassword
? `${hasUsername ? '***' : ''}${hasPassword ? `${hasUsername ? ':' : ''}***` : ''}@`
: '';
const searchSuffix = redactedSearchParams.size > 0 ? `?${redactedSearchParams.toString()}` : '';
const hashSuffix = parsed.hash ? '#REDACTED' : '';
return parsed.toString();
return `${parsed.protocol}//${authPrefix}${parsed.host}${parsed.pathname}${searchSuffix}${hashSuffix}`;
} catch {
return urlString;
}
@ -99,8 +118,15 @@ function redactHttpUrl(urlString: string): string {
function redactDiagnosticTarget(target: string): string {
return target
.replace(EMBEDDED_HTTP_URL_PATTERN, (match) => redactHttpUrl(match))
.replace(SENSITIVE_FLAG_VALUE_PATTERN, (_match, flag: string, inlineValue?: string) =>
inlineValue ? `${flag}=REDACTED` : `${flag} REDACTED`
.replace(
SENSITIVE_FLAG_VALUE_PATTERN,
(match, flag: string, inlineValue?: string, separatedValue?: string) => {
if (!isSensitiveCliFlag(flag)) {
return match;
}
return inlineValue || separatedValue ? `${flag}=REDACTED` : `${flag} REDACTED`;
}
);
}

View file

@ -51,8 +51,8 @@ import type {
CliInstallationStatus,
CliInstallerProgress,
CliPlatform,
CliProviderModelAvailability,
CliProviderId,
CliProviderModelAvailability,
CliProviderStatus,
} from '@shared/types';
import type { BrowserWindow } from 'electron';
@ -610,7 +610,7 @@ export class CliInstallerService {
private updateLatestProviderStatus(providerStatus: CliProviderStatus): void {
if (
providerStatus.modelVerificationState !== 'verifying' &&
!((providerStatus.modelAvailability?.length ?? 0) > 0)
(providerStatus.modelAvailability?.length ?? 0) <= 0
) {
this.latestProviderSignatures.set(providerStatus.providerId, null);
}

View file

@ -33,6 +33,7 @@ import {
import { getMemberColorByName } from '@shared/constants/memberColors';
import { DEFAULT_TOOL_APPROVAL_SETTINGS } from '@shared/types/team';
import { resolveLanguageName } from '@shared/utils/agentLanguage';
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
import { parseCliArgs } from '@shared/utils/cliArgsParser';
import {
isInboxNoiseMessage,
@ -42,14 +43,13 @@ import {
} from '@shared/utils/inboxNoise';
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
import { createLogger } from '@shared/utils/logger';
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
import { isDefaultProviderModelSelection } from '@shared/utils/providerModelSelection';
import { formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
import {
parseAllTeammateMessages,
type ParsedTeammateContent,
} from '@shared/utils/teammateMessageParser';
import { createCliAutoSuffixNameGuard, parseNumericSuffixName } from '@shared/utils/teamMemberName';
import { isDefaultProviderModelSelection } from '@shared/utils/providerModelSelection';
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
import {
extractToolPreview,
@ -68,16 +68,16 @@ import {
type GeminiRuntimeAuthState,
resolveGeminiRuntimeAuth,
} from '../runtime/geminiRuntimeAuth';
import { buildProviderAwareCliEnv } from '../runtime/providerAwareCliEnv';
import {
buildProviderPreflightPingArgs,
buildProviderModelProbeArgs,
buildProviderPreflightPingArgs,
classifyProviderModelProbeFailure,
getProviderModelProbeExpectedOutput,
getProviderModelProbeTimeoutMs,
isProviderModelProbeSuccessOutput,
normalizeProviderModelProbeFailureReason,
} from '../runtime/providerModelProbe';
import { buildProviderAwareCliEnv } from '../runtime/providerAwareCliEnv';
import { resolveTeamProviderId } from '../runtime/providerRuntimeEnv';
import { buildActionModeProtocol } from './actionModeInstructions';

View file

@ -145,7 +145,7 @@ export class TeammateToolTracker {
const state = this.stateByTeam.get(teamName);
if (!state?.enabled || state.epoch !== expectedEpoch) return;
const attributedFiles = await this.logsFinder.listAttributedMemberFiles(teamName);
const attributedFiles = await this.logsFinder.listAttributedSubagentFiles(teamName);
const currentState = this.stateByTeam.get(teamName);
if (!currentState?.enabled || currentState.epoch !== expectedEpoch) return;

View file

@ -12,7 +12,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { api, isElectronMode } from '@renderer/api';
import { confirm } from '@renderer/components/common/ConfirmDialog';
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
import {
formatProviderStatusText,
getProviderConnectionModeSummary,
@ -23,6 +22,7 @@ import {
isConnectionManagedRuntimeProvider,
shouldShowProviderConnectAction,
} from '@renderer/components/runtime/providerConnectionUi';
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
import { getProviderRuntimeBackendSummary } from '@renderer/components/runtime/ProviderRuntimeBackendSelector';
import { ProviderRuntimeSettingsDialog } from '@renderer/components/runtime/ProviderRuntimeSettingsDialog';
import { SettingsToggle } from '@renderer/components/settings/components';

View file

@ -11,12 +11,12 @@ import { Button } from '@renderer/components/ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
import { useStore } from '@renderer/store';
import { formatCompactNumber, formatRelativeTime } from '@renderer/utils/formatters';
import { getDefaultMcpSharedScope } from '@shared/utils/mcpScopes';
import {
getMcpInstallationSummaryLabel,
getMcpOperationKey,
sanitizeMcpServerName,
} from '@shared/utils/extensionNormalizers';
import { getDefaultMcpSharedScope } from '@shared/utils/mcpScopes';
import { Clock, Cloud, Globe, KeyRound, Lock, Monitor, Star, Tag, Wrench } from 'lucide-react';
import { Github as GithubIcon } from 'lucide-react';

View file

@ -25,18 +25,18 @@ import {
SelectValue,
} from '@renderer/components/ui/select';
import { useStore } from '@renderer/store';
import {
getDefaultMcpSharedScope,
getMcpScopeLabel,
isProjectScopedMcpScope,
isSharedMcpScope,
} from '@shared/utils/mcpScopes';
import {
getMcpInstallationSummaryLabel,
getMcpOperationKey,
getPreferredMcpInstallationEntry,
sanitizeMcpServerName,
} from '@shared/utils/extensionNormalizers';
import {
getDefaultMcpSharedScope,
getMcpScopeLabel,
isProjectScopedMcpScope,
isSharedMcpScope,
} from '@shared/utils/mcpScopes';
import { ExternalLink, Lock, Plus, Star, Trash2, Wrench } from 'lucide-react';
import { InstallButton } from '../common/InstallButton';

View file

@ -5,8 +5,8 @@
import { Badge } from '@renderer/components/ui/badge';
import { useStore } from '@renderer/store';
import {
getInstallationSummaryLabel,
getCapabilityLabel,
getInstallationSummaryLabel,
getPluginOperationKey,
hasInstallationInScope,
inferCapabilities,

View file

@ -25,8 +25,8 @@ import {
} from '@renderer/components/ui/select';
import { useStore } from '@renderer/store';
import {
getInstallationSummaryLabel,
getCapabilityLabel,
getInstallationSummaryLabel,
getPluginOperationKey,
hasInstallationInScope,
inferCapabilities,

View file

@ -16,8 +16,8 @@ import {
SelectValue,
} from '@renderer/components/ui/select';
import { useStore } from '@renderer/store';
import { getCliProviderExtensionCapability } from '@shared/utils/providerExtensionCapabilities';
import { inferCapabilities, normalizeCategory } from '@shared/utils/extensionNormalizers';
import { getCliProviderExtensionCapability } from '@shared/utils/providerExtensionCapabilities';
import { ArrowUpDown, Filter, Puzzle, Search } from 'lucide-react';
import { useShallow } from 'zustand/react/shallow';

View file

@ -29,6 +29,8 @@ import { useShallow } from 'zustand/react/shallow';
import { resolveSkillProjectPath } from './skillProjectUtils';
import type { SkillValidationIssue } from '@shared/types';
interface SkillDetailDialogProps {
skillId: string | null;
open: boolean;
@ -93,7 +95,7 @@ export const SkillDetailDialog = ({
: 'Runs automatically when it matches the task.';
}
function getIssuesTone(issues: typeof item.issues): {
function getIssuesTone(issues: SkillValidationIssue[]): {
className: string;
title: string;
Icon: typeof AlertTriangle;

View file

@ -41,8 +41,8 @@ import { validateSkillFolderName } from './skillValidationUtils';
import type {
SkillDetail,
SkillInvocationMode,
SkillRootKind,
SkillReviewPreview,
SkillRootKind,
} from '@shared/types/extensions';
type EditorMode = 'create' | 'edit';

View file

@ -24,8 +24,8 @@ import { SKILL_ROOT_DEFINITIONS } from '@shared/utils/skillRoots';
import { FileSearch, FolderOpen, X } from 'lucide-react';
import { getSuggestedSkillFolderNameFromPath } from './skillFolderNameUtils';
import { SkillReviewDialog } from './SkillReviewDialog';
import { resolveSkillProjectPath } from './skillProjectUtils';
import { SkillReviewDialog } from './SkillReviewDialog';
import { validateSkillFolderName, validateSkillImportSourceDir } from './skillValidationUtils';
import type { SkillReviewPreview, SkillRootKind } from '@shared/types/extensions';

View file

@ -127,7 +127,7 @@ function formatRuntimeAudienceLabel(providerNames: readonly string[]): string {
return 'the configured runtime';
}
if (providerNames.length === 1) {
return providerNames[0]!;
return providerNames[0];
}
if (providerNames.length === 2) {
return `${providerNames[0]} and ${providerNames[1]}`;
@ -165,7 +165,7 @@ export const SkillsPanel = ({
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const [highlightedSkillId, setHighlightedSkillId] = useState<string | null>(null);
const selectedSkillIdRef = useRef<string | null>(selectedSkillId);
const selectedSkillItemRef = useRef<SkillCatalogItem | SkillDetail['item'] | null>(null);
const selectedSkillItemRef = useRef<SkillCatalogItem | null>(null);
selectedSkillIdRef.current = selectedSkillId;
const mergedSkills = useMemo(

View file

@ -1,8 +1,8 @@
import { cn } from '@renderer/lib/utils';
import {
getTeamModelBadgeLabel,
getVisibleTeamProviderModels,
} from '@renderer/utils/teamModelCatalog';
import { cn } from '@renderer/lib/utils';
import type {
CliProviderId,
@ -43,7 +43,7 @@ function getAvailabilityChip(status: CliProviderModelAvailabilityStatus | null):
}
}
export function ProviderModelBadges({
export const ProviderModelBadges = ({
providerId,
models,
modelAvailability,
@ -53,7 +53,7 @@ export function ProviderModelBadges({
readonly models: string[];
readonly modelAvailability?: CliProviderModelAvailability[];
readonly providerStatus?: Pick<CliProviderStatus, 'providerId' | 'authMethod' | 'backend'> | null;
}): React.JSX.Element {
}): React.JSX.Element => {
const visibleModels = getVisibleTeamProviderModels(providerId, models, providerStatus);
return (
@ -94,4 +94,4 @@ export function ProviderModelBadges({
})}
</div>
);
}
};

View file

@ -10,7 +10,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { isElectronMode } from '@renderer/api';
import { confirm } from '@renderer/components/common/ConfirmDialog';
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
import {
formatProviderStatusText,
getProviderConnectionModeSummary,
@ -21,6 +20,7 @@ import {
isConnectionManagedRuntimeProvider,
shouldShowProviderConnectAction,
} from '@renderer/components/runtime/providerConnectionUi';
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
import { getProviderRuntimeBackendSummary } from '@renderer/components/runtime/ProviderRuntimeBackendSelector';
import { ProviderRuntimeSettingsDialog } from '@renderer/components/runtime/ProviderRuntimeSettingsDialog';
import { SettingsToggle } from '@renderer/components/settings/components';

View file

@ -45,7 +45,10 @@ import {
getTeamModelSelectionError,
normalizeTeamModelForUi,
} from '@renderer/utils/teamModelAvailability';
import { getTeamProviderLabel as getCatalogTeamProviderLabel } from '@renderer/utils/teamModelCatalog';
import {
getTeamProviderLabel as getCatalogTeamProviderLabel,
normalizeTeamModelForUi as normalizeCatalogTeamModelForUi,
} from '@renderer/utils/teamModelCatalog';
import { DEFAULT_PROVIDER_MODEL_SELECTION } from '@shared/utils/providerModelSelection';
import { isTeamProviderId, normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
import { AlertTriangle, CheckCircle2, Info, Loader2, X } from 'lucide-react';
@ -53,22 +56,22 @@ import { AlertTriangle, CheckCircle2, Info, Loader2, X } from 'lucide-react';
import { AdvancedCliSection } from './AdvancedCliSection';
import { OptionalSettingsSection } from './OptionalSettingsSection';
import { ProjectPathSelector } from './ProjectPathSelector';
import {
getProviderPrepareCachedSnapshot,
type ProviderPrepareDiagnosticsModelResult,
runProviderPrepareDiagnostics,
} from './providerPrepareDiagnostics';
import { getProvisioningModelIssue } from './provisioningModelIssues';
import {
failIncompleteProviderChecks,
getProvisioningFailureHint,
getPrimaryProvisioningFailureDetail,
getProvisioningFailureHint,
getProvisioningProviderBackendSummary,
type ProvisioningProviderCheck,
ProvisioningProviderStatusList,
shouldHideProvisioningProviderStatusList,
updateProviderCheck,
} from './ProvisioningProviderStatusList';
import { getProvisioningModelIssue } from './provisioningModelIssues';
import {
getProviderPrepareCachedSnapshot,
runProviderPrepareDiagnostics,
type ProviderPrepareDiagnosticsModelResult,
} from './providerPrepareDiagnostics';
import { SkipPermissionsCheckbox } from './SkipPermissionsCheckbox';
import { computeEffectiveTeamModel } from './TeamModelSelector';
import { getNextSuggestedTeamName } from './teamNameSets';
@ -108,7 +111,7 @@ function getStoredTeamModel(providerId: TeamProviderId): string {
if (stored === null) {
return providerId === 'anthropic' ? 'opus' : '';
}
return normalizeTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
return normalizeCatalogTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
}
function isEphemeralRenderedProjectPath(projectPath: string | null | undefined): boolean {

View file

@ -47,7 +47,10 @@ import {
getTeamModelSelectionError,
normalizeTeamModelForUi,
} from '@renderer/utils/teamModelAvailability';
import { getTeamProviderLabel as getCatalogTeamProviderLabel } from '@renderer/utils/teamModelCatalog';
import {
getTeamProviderLabel as getCatalogTeamProviderLabel,
normalizeTeamModelForUi as normalizeCatalogTeamModelForUi,
} from '@renderer/utils/teamModelCatalog';
import { DEFAULT_PROVIDER_MODEL_SELECTION } from '@shared/utils/providerModelSelection';
import { isTeamProviderId, normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
import {
@ -69,22 +72,22 @@ import { EffortLevelSelector } from './EffortLevelSelector';
import { resolveLaunchDialogPrefill } from './launchDialogPrefill';
import { OptionalSettingsSection } from './OptionalSettingsSection';
import { ProjectPathSelector } from './ProjectPathSelector';
import {
getProviderPrepareCachedSnapshot,
type ProviderPrepareDiagnosticsModelResult,
runProviderPrepareDiagnostics,
} from './providerPrepareDiagnostics';
import { getProvisioningModelIssue } from './provisioningModelIssues';
import {
failIncompleteProviderChecks,
getProvisioningFailureHint,
getPrimaryProvisioningFailureDetail,
getProvisioningFailureHint,
getProvisioningProviderBackendSummary,
type ProvisioningProviderCheck,
ProvisioningProviderStatusList,
shouldHideProvisioningProviderStatusList,
updateProviderCheck,
} from './ProvisioningProviderStatusList';
import { getProvisioningModelIssue } from './provisioningModelIssues';
import {
getProviderPrepareCachedSnapshot,
runProviderPrepareDiagnostics,
type ProviderPrepareDiagnosticsModelResult,
} from './providerPrepareDiagnostics';
import {
computeEffectiveTeamModel,
formatTeamModelSummary,
@ -192,7 +195,7 @@ function getStoredTeamModel(providerId: TeamProviderId): string {
if (stored === null) {
return providerId === 'anthropic' ? 'opus' : '';
}
return normalizeTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
return normalizeCatalogTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
}
function getProviderLabel(providerId: TeamProviderId): string {

View file

@ -11,7 +11,6 @@ import {
} from '@renderer/components/ui/tooltip';
import { cn } from '@renderer/lib/utils';
import { useStore } from '@renderer/store';
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
import {
GEMINI_UI_DISABLED_BADGE_LABEL,
GEMINI_UI_DISABLED_REASON,
@ -30,6 +29,7 @@ import {
getTeamProviderLabel as getCatalogTeamProviderLabel,
} from '@renderer/utils/teamModelCatalog';
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
import { AlertTriangle, Info } from 'lucide-react';
export { getProviderScopedTeamModelLabel } from '@renderer/utils/teamModelCatalog';

View file

@ -1,5 +1,5 @@
import { normalizeCreateLaunchProviderForUi } from '@renderer/utils/geminiUiFreeze';
import { normalizeTeamModelForUi } from '@renderer/utils/teamModelAvailability';
import { normalizeTeamModelForUi as normalizeCatalogTeamModelForUi } from '@renderer/utils/teamModelCatalog';
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
import { isLeadMember } from '@shared/utils/leadDetection';
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
@ -102,7 +102,7 @@ export function resolveLaunchDialogPrefill({
return {
providerId,
model: matchingModel
? normalizeTeamModelForUi(providerId, matchingModel)
? normalizeCatalogTeamModelForUi(providerId, matchingModel)
: getStoredModel(providerId),
effort,
limitContext,

View file

@ -5,15 +5,13 @@ import type { TeamProviderId, TeamProvisioningPrepareResult } from '@shared/type
export type ProviderPrepareCheckStatus = 'ready' | 'notes' | 'failed';
interface PrepareProvisioningFn {
(
cwd?: string,
providerId?: TeamProviderId,
providerIds?: TeamProviderId[],
selectedModels?: string[],
limitContext?: boolean
): Promise<TeamProvisioningPrepareResult>;
}
type PrepareProvisioningFn = (
cwd?: string,
providerId?: TeamProviderId,
providerIds?: TeamProviderId[],
selectedModels?: string[],
limitContext?: boolean
) => Promise<TeamProvisioningPrepareResult>;
interface ProviderPrepareDiagnosticsProgress {
details: string[];
@ -156,15 +154,15 @@ function normalizeModelReason(rawReason: string | null | undefined): string | nu
return 'Model verification timed out';
}
const detailMatch = trimmed.match(/"detail":"((?:\\"|[^"])*)"/i);
const detailMatch = /"detail":"((?:\\"|[^"])*)"/i.exec(trimmed);
if (detailMatch?.[1]) {
return normalizeModelReason(detailMatch[1].replace(/\\"/g, '"').trim());
}
const messageMatch = trimmed.match(/"message":"((?:\\"|[^"])*)"/i);
const messageMatch = /"message":"((?:\\"|[^"])*)"/i.exec(trimmed);
if (messageMatch?.[1]) {
const decodedMessage = messageMatch[1].replace(/\\"/g, '"');
const nestedDetailMatch = decodedMessage.match(/"detail":"([^"]+)"/i);
const nestedDetailMatch = /"detail":"([^"]+)"/i.exec(decodedMessage);
if (nestedDetailMatch?.[1]) {
return normalizeModelReason(nestedDetailMatch[1].trim());
}

View file

@ -111,8 +111,7 @@ export const MemberCard = ({
!isRemoved &&
presenceLabel === 'starting' &&
spawnLaunchState !== 'failed_to_start' &&
!activityTask &&
!runtimeSummary;
!activityTask;
const showStartingBadge = !isRemoved && presenceLabel === 'starting' && !activityTask;
const showRuntimeAdvisoryBadge =
!isRemoved &&

View file

@ -16,8 +16,8 @@ import { getTeamColorSet } from '@renderer/constants/teamColors';
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer';
import { useTheme } from '@renderer/hooks/useTheme';
import { reconcileChips, removeChipTokenFromText } from '@renderer/utils/chipUtils';
import { cn } from '@renderer/lib/utils';
import { reconcileChips, removeChipTokenFromText } from '@renderer/utils/chipUtils';
import { getMemberColorByName } from '@shared/constants/memberColors';
import { AlertTriangle, ChevronDown, ChevronRight, Info, RotateCcw, Trash2 } from 'lucide-react';

View file

@ -1,7 +1,7 @@
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { resolveMemberRuntimeSummary } from '@renderer/utils/memberRuntimeSummary';
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
import { resolveMemberRuntimeSummary } from '@renderer/utils/memberRuntimeSummary';
import { isLeadMember } from '@shared/utils/leadDetection';
import { MemberCard } from './MemberCard';

View file

@ -3,6 +3,8 @@ import { serializeChipsWithText } from '@renderer/types/inlineChip';
import { normalizeCreateLaunchProviderForUi } from '@renderer/utils/geminiUiFreeze';
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
import { normalizeTeamModelForUi } from '@renderer/utils/teamModelAvailability';
import { normalizeTeamModelForUi as normalizeCatalogTeamModelForUi } from '@renderer/utils/teamModelCatalog';
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
import { isLeadMember } from '@shared/utils/leadDetection';
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
@ -32,6 +34,7 @@ function newDraftId(): string {
export function createMemberDraft(initial?: Partial<MemberDraft>): MemberDraft {
const providerId = initial?.providerId;
const normalizedModel = extractProviderScopedBaseModel(initial?.model ?? '', providerId) ?? '';
return {
id: initial?.id ?? newDraftId(),
name: initial?.name ?? '',
@ -39,7 +42,7 @@ export function createMemberDraft(initial?: Partial<MemberDraft>): MemberDraft {
customRole: initial?.customRole ?? '',
workflow: initial?.workflow,
providerId,
model: normalizeTeamModelForUi(providerId, initial?.model ?? ''),
model: normalizeCatalogTeamModelForUi(providerId, normalizedModel),
effort: initial?.effort,
removedAt: initial?.removedAt,
};

View file

@ -4,14 +4,14 @@
*/
import { api } from '@renderer/api';
import { isProjectScopedMcpScope } from '@shared/utils/mcpScopes';
import {
getExtensionActionDisableReason,
getMcpDiagnosticKey,
getMcpProjectStateKey,
getMcpOperationKey,
getMcpProjectStateKey,
getPluginOperationKey,
} from '@shared/utils/extensionNormalizers';
import { isProjectScopedMcpScope } from '@shared/utils/mcpScopes';
import { findPaneByTabId, updatePane } from '../utils/paneHelpers';

View file

@ -94,10 +94,10 @@ type TeamGraphConfigMemberSeedInput = Pick<
NonNullable<TeamData['config']['members']>[number],
'name' | 'agentId' | 'removedAt'
>;
type TeamGraphLayoutSessionState = {
interface TeamGraphLayoutSessionState {
mode: 'default' | 'manual';
signature: string | null;
};
}
export function isTeamDataRefreshPending(teamName: string): boolean {
return (
@ -1025,8 +1025,7 @@ function areTeamGraphSlotAssignmentsEqual(
for (const [stableOwnerId, leftAssignment] of leftEntries) {
const rightAssignment = right?.[stableOwnerId];
if (
!rightAssignment ||
rightAssignment.ringIndex !== leftAssignment.ringIndex ||
rightAssignment?.ringIndex !== leftAssignment.ringIndex ||
rightAssignment.sectorIndex !== leftAssignment.sectorIndex
) {
return false;
@ -2063,8 +2062,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
return (
(nextAssignment.ringIndex === assignment.ringIndex &&
nextAssignment.sectorIndex === assignment.sectorIndex) ||
(displacedAssignment != null &&
nextAssignment.ringIndex === displacedAssignment.ringIndex &&
(nextAssignment.ringIndex === displacedAssignment?.ringIndex &&
nextAssignment.sectorIndex === displacedAssignment.sectorIndex)
);
}

View file

@ -1,8 +1,8 @@
import { formatTeamModelSummary } from '@renderer/components/team/dialogs/TeamModelSelector';
import { inferTeamProviderIdFromModel } from '@shared/utils/teamProvider';
import type { TeamLaunchParams } from '@renderer/store/slices/teamSlice';
import type { MemberSpawnStatusEntry, ResolvedTeamMember, TeamProviderId } from '@shared/types';
import { inferTeamProviderIdFromModel } from '@shared/utils/teamProvider';
function isMemberLaunchPending(spawnEntry: MemberSpawnStatusEntry | undefined): boolean {
if (!spawnEntry) {

View file

@ -2,8 +2,8 @@ import { getSkillAudienceLabel, isSkillAvailableForProvider } from '@shared/util
import { isSupportedSlashCommandName } from '@shared/utils/slashCommands';
import type { MentionSuggestion } from '@renderer/types/mention';
import type { SkillCatalogItem } from '@shared/types/extensions';
import type { TeamProviderId } from '@shared/types';
import type { SkillCatalogItem } from '@shared/types/extensions';
import type { KnownSlashCommandDefinition } from '@shared/utils/slashCommands';
function orderSkillsForProvider(

View file

@ -1,3 +1,22 @@
import {
getProviderScopedTeamModelLabel,
getRuntimeAwareTeamModelUiDisabledReason,
getTeamProviderLabel,
getTeamProviderModelOptions,
getVisibleTeamProviderModels,
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
GPT_5_2_CODEX_UI_DISABLED_MODEL,
GPT_5_2_CODEX_UI_DISABLED_REASON,
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON,
normalizeTeamModelForUi as normalizeCatalogTeamModelForUi,
sortTeamProviderModels,
TEAM_MODEL_UI_DISABLED_BADGE_LABEL,
type TeamProviderModelOption,
} from './teamModelCatalog';
import type {
CliProviderId,
CliProviderModelAvailability,
@ -6,29 +25,10 @@ import type {
TeamProviderId,
} from '@shared/types';
import {
getProviderScopedTeamModelLabel,
getRuntimeAwareTeamModelUiDisabledReason,
getTeamProviderLabel,
getTeamProviderModelOptions,
sortTeamProviderModels,
getVisibleTeamProviderModels,
normalizeTeamModelForUi as normalizeCatalogTeamModelForUi,
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
GPT_5_2_CODEX_UI_DISABLED_MODEL,
GPT_5_2_CODEX_UI_DISABLED_REASON,
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON,
TEAM_MODEL_UI_DISABLED_BADGE_LABEL,
type TeamProviderModelOption,
} from './teamModelCatalog';
export {
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
GPT_5_2_CODEX_UI_DISABLED_MODEL,
GPT_5_2_CODEX_UI_DISABLED_REASON,
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,

View file

@ -1,4 +1,3 @@
import type { CliProviderId, CliProviderStatus, TeamProviderId } from '@shared/types';
import {
filterVisibleProviderRuntimeModels,
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
@ -6,6 +5,8 @@ import {
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
} from '@shared/utils/providerModelVisibility';
import type { CliProviderId, CliProviderStatus, TeamProviderId } from '@shared/types';
export {
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
GPT_5_2_CODEX_UI_DISABLED_MODEL,

View file

@ -9,9 +9,9 @@ import {
import type {
CliInstallationStatus,
InstallScope,
InstalledMcpEntry,
InstalledPluginEntry,
InstallScope,
PluginCapability,
PluginCatalogItem,
} from '@shared/types';
@ -206,7 +206,7 @@ export function getPreferredMcpInstallationEntry(
return [...installations].sort(
(left, right) => MCP_SCOPE_PRIORITY[left.scope] - MCP_SCOPE_PRIORITY[right.scope]
)[0]!;
)[0];
}
/**

View file

@ -231,7 +231,7 @@ describe('CliInstallerService', () => {
verificationState: 'verified',
modelVerificationState: 'idle',
statusMessage: null,
models: ['gpt-5.4', 'gpt-5.2-codex'],
models: ['gpt-5.4', 'gpt-5.4-mini'],
modelAvailability: [],
canLoginFromUi: true,
capabilities: { teamLaunch: true, oneShot: true },
@ -267,14 +267,12 @@ describe('CliInstallerService', () => {
if (normalizedArgs === '--version') {
return { stdout: '2.3.4', stderr: '' };
}
if (normalizedArgs.includes('--model gpt-5.4-mini')) {
throw new Error("The 'gpt-5.4-mini' model is not supported in this Codex runtime.");
}
if (normalizedArgs.includes('--model gpt-5.4')) {
return { stdout: 'PONG', stderr: '' };
}
if (normalizedArgs.includes('--model gpt-5.2-codex')) {
throw new Error(
"The 'gpt-5.2-codex' model is not supported when using Codex with a ChatGPT account."
);
}
throw new Error(`Unexpected execCli call: ${normalizedArgs}`);
});
@ -291,7 +289,7 @@ describe('CliInstallerService', () => {
expect(verifiedProvider?.modelAvailability).toEqual(
expect.arrayContaining([
expect.objectContaining({ modelId: 'gpt-5.4', status: 'checking' }),
expect.objectContaining({ modelId: 'gpt-5.2-codex', status: 'checking' }),
expect.objectContaining({ modelId: 'gpt-5.4-mini', status: 'checking' }),
])
);
@ -303,7 +301,7 @@ describe('CliInstallerService', () => {
expect(latestCodexProvider?.modelAvailability).toEqual([
expect.objectContaining({ modelId: 'gpt-5.4', status: 'available' }),
expect.objectContaining({
modelId: 'gpt-5.2-codex',
modelId: 'gpt-5.4-mini',
status: 'unavailable',
}),
]);

View file

@ -40,7 +40,7 @@ interface StoreState {
installed?: boolean;
binaryPath?: string | null;
launchError?: string | null;
};
} | null;
}
const storeState = {} as StoreState;

View file

@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { CliInstallationStatus } from '@shared/types';
import type { SkillCatalogItem } from '@shared/types/extensions';
import { createDefaultCliExtensionCapabilities } from '@shared/utils/providerExtensionCapabilities';
interface StoreState {
fetchSkillsCatalog: ReturnType<typeof vi.fn>;
@ -213,12 +214,9 @@ function makeMultimodelStatus(
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: { status: 'supported', ownership: 'provider', reason: null },
mcp: { status: 'supported', ownership: 'shared', reason: null },
skills: { status: 'supported', ownership: 'shared', reason: null },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
extensions: createDefaultCliExtensionCapabilities({
plugins: { status: 'supported', ownership: 'provider-scoped', reason: null },
}),
},
connection: null,
backend: null,
@ -405,12 +403,9 @@ describe('SkillsPanel', () => {
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: { status: 'unsupported', ownership: 'provider', reason: null },
mcp: { status: 'supported', ownership: 'shared', reason: null },
skills: { status: 'supported', ownership: 'shared', reason: null },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
extensions: createDefaultCliExtensionCapabilities({
plugins: { status: 'unsupported', ownership: 'provider-scoped', reason: null },
}),
},
connection: null,
backend: null,

View file

@ -55,12 +55,14 @@ vi.mock('../../../src/renderer/api', () => ({
}));
import { api } from '../../../src/renderer/api';
import type { CliInstallationStatus } from '../../../src/shared/types';
import {
getMcpDiagnosticKey,
getMcpProjectStateKey,
getMcpOperationKey,
getPluginOperationKey,
} from '../../../src/shared/utils/extensionNormalizers';
import { createDefaultCliExtensionCapabilities } from '../../../src/shared/utils/providerExtensionCapabilities';
import type {
EnrichedPlugin,
@ -137,7 +139,7 @@ const makeSkillDetail = (overrides: Partial<SkillDetail> = {}): SkillDetail => (
...overrides,
});
const makeReadyCliStatus = () => ({
const makeReadyCliStatus = (): CliInstallationStatus => ({
flavor: 'claude' as const,
displayName: 'Claude',
supportsSelfUpdate: true,
@ -154,7 +156,10 @@ const makeReadyCliStatus = () => ({
providers: [],
});
const makeLimitedMultimodelCliStatus = (section: 'plugins' | 'mcp', reason: string) => ({
const makeLimitedMultimodelCliStatus = (
section: 'plugins' | 'mcp',
reason: string
): CliInstallationStatus => ({
flavor: 'agent_teams_orchestrator' as const,
displayName: 'Claude Multimodel',
supportsSelfUpdate: false,
@ -181,21 +186,22 @@ const makeLimitedMultimodelCliStatus = (section: 'plugins' | 'mcp', reason: stri
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
extensions: createDefaultCliExtensionCapabilities({
plugins: {
status: section === 'plugins' ? 'unsupported' : 'supported',
ownership: 'shared' as const,
ownership: 'shared',
reason: section === 'plugins' ? reason : null,
},
mcp: {
status: section === 'mcp' ? 'read-only' : 'supported',
ownership: 'shared' as const,
ownership: 'shared',
reason: section === 'mcp' ? reason : null,
},
skills: { status: 'supported', ownership: 'shared' as const, reason: null },
apiKeys: { status: 'supported', ownership: 'shared' as const, reason: null },
},
}),
},
statusMessage: null,
connection: null,
backend: null,
},
],
});

View file

@ -7,6 +7,7 @@ import {
} from '@renderer/utils/multimodelProviderVisibility';
import type { CliInstallationStatus, CliProviderStatus } from '@shared/types';
import { createDefaultCliExtensionCapabilities } from '@shared/utils/providerExtensionCapabilities';
function createProvider(providerId: CliProviderStatus['providerId']): CliProviderStatus {
return {
@ -21,9 +22,9 @@ function createProvider(providerId: CliProviderStatus['providerId']): CliProvide
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: createDefaultCliExtensionCapabilities(),
},
statusMessage: null,
detailMessage: null,
selectedBackendId: null,
resolvedBackendId: null,
availableBackends: [],