feat(adapters): add capability flags to ServerAdapterModule
Replace 5 hardcoded adapter type lists with declarative capability flags on ServerAdapterModule, enabling external adapter plugins to declare their capabilities without modifying Paperclip source. New optional fields on ServerAdapterModule: - supportsInstructionsBundle: managed instructions bundle support - instructionsPathKey: config key for instructions file path - requiresMaterializedRuntimeSkills: skill materialization needed Server changes: - agents.ts: capability-aware helpers with legacy fallbacks - adapters.ts: expose capabilities in GET /api/adapters response - registry.ts: explicit flags on all built-in adapters UI changes: - New useAdapterCapabilities hook for capability lookups - AgentDetail.tsx: replace hardcoded isLocal allowlist - AgentConfigForm.tsx: replace NONLOCAL_TYPES denylist - OnboardingWizard.tsx: replace NONLOCAL_TYPES denylist All flags are optional with backwards-compatible fallbacks to the legacy hardcoded lists for adapters that don't set them. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -328,6 +328,36 @@ export interface ServerAdapterModule {
|
|||||||
* resolved inside this method — the caller receives a fully hydrated schema.
|
* resolved inside this method — the caller receives a fully hydrated schema.
|
||||||
*/
|
*/
|
||||||
getConfigSchema?: () => Promise<AdapterConfigSchema> | AdapterConfigSchema;
|
getConfigSchema?: () => Promise<AdapterConfigSchema> | AdapterConfigSchema;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Adapter capability flags
|
||||||
|
//
|
||||||
|
// These allow adapter plugins to declare what "local" capabilities they
|
||||||
|
// support, replacing hardcoded type lists in the server and UI.
|
||||||
|
// All flags are optional — when undefined, the server falls back to
|
||||||
|
// legacy hardcoded lists for built-in adapters.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter supports managed instructions bundle (AGENTS.md files).
|
||||||
|
* When true, the server uses instructionsPathKey (default "instructionsFilePath")
|
||||||
|
* to resolve the instructions config key, and the UI shows the bundle editor.
|
||||||
|
* Built-in local adapters default to true; external plugins must opt in.
|
||||||
|
*/
|
||||||
|
supportsInstructionsBundle?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The adapterConfig key that holds the instructions file path.
|
||||||
|
* Defaults to "instructionsFilePath" when supportsInstructionsBundle is true.
|
||||||
|
*/
|
||||||
|
instructionsPathKey?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter needs runtime skill entries materialized (written to disk)
|
||||||
|
* before being passed via config. Used by adapters that scan a directory
|
||||||
|
* rather than reading config.paperclipRuntimeSkills.
|
||||||
|
*/
|
||||||
|
requiresMaterializedRuntimeSkills?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ const claudeLocalAdapter: ServerAdapterModule = {
|
|||||||
models: claudeModels,
|
models: claudeModels,
|
||||||
listModels: listClaudeModels,
|
listModels: listClaudeModels,
|
||||||
supportsLocalAgentJwt: true,
|
supportsLocalAgentJwt: true,
|
||||||
|
supportsInstructionsBundle: true,
|
||||||
|
instructionsPathKey: "instructionsFilePath",
|
||||||
|
requiresMaterializedRuntimeSkills: false,
|
||||||
agentConfigurationDoc: claudeAgentConfigurationDoc,
|
agentConfigurationDoc: claudeAgentConfigurationDoc,
|
||||||
getQuotaWindows: claudeGetQuotaWindows,
|
getQuotaWindows: claudeGetQuotaWindows,
|
||||||
};
|
};
|
||||||
@@ -112,6 +115,9 @@ const codexLocalAdapter: ServerAdapterModule = {
|
|||||||
models: codexModels,
|
models: codexModels,
|
||||||
listModels: listCodexModels,
|
listModels: listCodexModels,
|
||||||
supportsLocalAgentJwt: true,
|
supportsLocalAgentJwt: true,
|
||||||
|
supportsInstructionsBundle: true,
|
||||||
|
instructionsPathKey: "instructionsFilePath",
|
||||||
|
requiresMaterializedRuntimeSkills: false,
|
||||||
agentConfigurationDoc: codexAgentConfigurationDoc,
|
agentConfigurationDoc: codexAgentConfigurationDoc,
|
||||||
getQuotaWindows: codexGetQuotaWindows,
|
getQuotaWindows: codexGetQuotaWindows,
|
||||||
};
|
};
|
||||||
@@ -127,6 +133,9 @@ const cursorLocalAdapter: ServerAdapterModule = {
|
|||||||
models: cursorModels,
|
models: cursorModels,
|
||||||
listModels: listCursorModels,
|
listModels: listCursorModels,
|
||||||
supportsLocalAgentJwt: true,
|
supportsLocalAgentJwt: true,
|
||||||
|
supportsInstructionsBundle: true,
|
||||||
|
instructionsPathKey: "instructionsFilePath",
|
||||||
|
requiresMaterializedRuntimeSkills: true,
|
||||||
agentConfigurationDoc: cursorAgentConfigurationDoc,
|
agentConfigurationDoc: cursorAgentConfigurationDoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,6 +149,9 @@ const geminiLocalAdapter: ServerAdapterModule = {
|
|||||||
sessionManagement: getAdapterSessionManagement("gemini_local") ?? undefined,
|
sessionManagement: getAdapterSessionManagement("gemini_local") ?? undefined,
|
||||||
models: geminiModels,
|
models: geminiModels,
|
||||||
supportsLocalAgentJwt: true,
|
supportsLocalAgentJwt: true,
|
||||||
|
supportsInstructionsBundle: true,
|
||||||
|
instructionsPathKey: "instructionsFilePath",
|
||||||
|
requiresMaterializedRuntimeSkills: true,
|
||||||
agentConfigurationDoc: geminiAgentConfigurationDoc,
|
agentConfigurationDoc: geminiAgentConfigurationDoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,6 +161,8 @@ const openclawGatewayAdapter: ServerAdapterModule = {
|
|||||||
testEnvironment: openclawGatewayTestEnvironment,
|
testEnvironment: openclawGatewayTestEnvironment,
|
||||||
models: openclawGatewayModels,
|
models: openclawGatewayModels,
|
||||||
supportsLocalAgentJwt: false,
|
supportsLocalAgentJwt: false,
|
||||||
|
supportsInstructionsBundle: false,
|
||||||
|
requiresMaterializedRuntimeSkills: false,
|
||||||
agentConfigurationDoc: openclawGatewayAgentConfigurationDoc,
|
agentConfigurationDoc: openclawGatewayAgentConfigurationDoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -163,6 +177,9 @@ const openCodeLocalAdapter: ServerAdapterModule = {
|
|||||||
sessionManagement: getAdapterSessionManagement("opencode_local") ?? undefined,
|
sessionManagement: getAdapterSessionManagement("opencode_local") ?? undefined,
|
||||||
listModels: listOpenCodeModels,
|
listModels: listOpenCodeModels,
|
||||||
supportsLocalAgentJwt: true,
|
supportsLocalAgentJwt: true,
|
||||||
|
supportsInstructionsBundle: true,
|
||||||
|
instructionsPathKey: "instructionsFilePath",
|
||||||
|
requiresMaterializedRuntimeSkills: true,
|
||||||
agentConfigurationDoc: openCodeAgentConfigurationDoc,
|
agentConfigurationDoc: openCodeAgentConfigurationDoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,6 +194,9 @@ const piLocalAdapter: ServerAdapterModule = {
|
|||||||
models: [],
|
models: [],
|
||||||
listModels: listPiModels,
|
listModels: listPiModels,
|
||||||
supportsLocalAgentJwt: true,
|
supportsLocalAgentJwt: true,
|
||||||
|
supportsInstructionsBundle: true,
|
||||||
|
instructionsPathKey: "instructionsFilePath",
|
||||||
|
requiresMaterializedRuntimeSkills: true,
|
||||||
agentConfigurationDoc: piAgentConfigurationDoc,
|
agentConfigurationDoc: piAgentConfigurationDoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -189,6 +209,9 @@ const hermesLocalAdapter: ServerAdapterModule = {
|
|||||||
syncSkills: hermesSyncSkills,
|
syncSkills: hermesSyncSkills,
|
||||||
models: hermesModels,
|
models: hermesModels,
|
||||||
supportsLocalAgentJwt: true,
|
supportsLocalAgentJwt: true,
|
||||||
|
supportsInstructionsBundle: true,
|
||||||
|
instructionsPathKey: "instructionsFilePath",
|
||||||
|
requiresMaterializedRuntimeSkills: false,
|
||||||
agentConfigurationDoc: hermesAgentConfigurationDoc,
|
agentConfigurationDoc: hermesAgentConfigurationDoc,
|
||||||
detectModel: () => detectModelFromHermes(),
|
detectModel: () => detectModelFromHermes(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,6 +59,13 @@ interface AdapterInstallRequest {
|
|||||||
version?: string;
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AdapterCapabilities {
|
||||||
|
supportsInstructionsBundle: boolean;
|
||||||
|
supportsSkills: boolean;
|
||||||
|
supportsLocalAgentJwt: boolean;
|
||||||
|
requiresMaterializedRuntimeSkills: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface AdapterInfo {
|
interface AdapterInfo {
|
||||||
type: string;
|
type: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -66,6 +73,7 @@ interface AdapterInfo {
|
|||||||
modelsCount: number;
|
modelsCount: number;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
capabilities: AdapterCapabilities;
|
||||||
/** True when an external plugin has replaced a built-in adapter of the same type. */
|
/** True when an external plugin has replaced a built-in adapter of the same type. */
|
||||||
overriddenBuiltin?: boolean;
|
overriddenBuiltin?: boolean;
|
||||||
/** True when the external override for a builtin type is currently paused. */
|
/** True when the external override for a builtin type is currently paused. */
|
||||||
@@ -103,6 +111,15 @@ function readAdapterPackageVersionFromDisk(record: AdapterPluginRecord): string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildAdapterCapabilities(adapter: ServerAdapterModule): AdapterCapabilities {
|
||||||
|
return {
|
||||||
|
supportsInstructionsBundle: adapter.supportsInstructionsBundle ?? false,
|
||||||
|
supportsSkills: Boolean(adapter.listSkills || adapter.syncSkills),
|
||||||
|
supportsLocalAgentJwt: adapter.supportsLocalAgentJwt ?? false,
|
||||||
|
requiresMaterializedRuntimeSkills: adapter.requiresMaterializedRuntimeSkills ?? false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildAdapterInfo(adapter: ServerAdapterModule, externalRecord: AdapterPluginRecord | undefined, disabledSet: Set<string>): AdapterInfo {
|
function buildAdapterInfo(adapter: ServerAdapterModule, externalRecord: AdapterPluginRecord | undefined, disabledSet: Set<string>): AdapterInfo {
|
||||||
const fromDisk = externalRecord ? readAdapterPackageVersionFromDisk(externalRecord) : undefined;
|
const fromDisk = externalRecord ? readAdapterPackageVersionFromDisk(externalRecord) : undefined;
|
||||||
return {
|
return {
|
||||||
@@ -112,6 +129,7 @@ function buildAdapterInfo(adapter: ServerAdapterModule, externalRecord: AdapterP
|
|||||||
modelsCount: (adapter.models ?? []).length,
|
modelsCount: (adapter.models ?? []).length,
|
||||||
loaded: true, // If it's in the registry, it's loaded
|
loaded: true, // If it's in the registry, it's loaded
|
||||||
disabled: disabledSet.has(adapter.type),
|
disabled: disabledSet.has(adapter.type),
|
||||||
|
capabilities: buildAdapterCapabilities(adapter),
|
||||||
overriddenBuiltin: externalRecord ? BUILTIN_ADAPTER_TYPES.has(adapter.type) : undefined,
|
overriddenBuiltin: externalRecord ? BUILTIN_ADAPTER_TYPES.has(adapter.type) : undefined,
|
||||||
overridePaused: BUILTIN_ADAPTER_TYPES.has(adapter.type) ? isOverridePaused(adapter.type) : undefined,
|
overridePaused: BUILTIN_ADAPTER_TYPES.has(adapter.type) ? isOverridePaused(adapter.type) : undefined,
|
||||||
// Prefer on-disk package.json so the UI reflects bumps without relying on store-only fields.
|
// Prefer on-disk package.json so the UI reflects bumps without relying on store-only fields.
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ import {
|
|||||||
import { getTelemetryClient } from "../telemetry.js";
|
import { getTelemetryClient } from "../telemetry.js";
|
||||||
|
|
||||||
export function agentRoutes(db: Db) {
|
export function agentRoutes(db: Db) {
|
||||||
|
// Legacy hardcoded maps — used as fallback when adapter module does not
|
||||||
|
// declare capability flags explicitly.
|
||||||
const DEFAULT_INSTRUCTIONS_PATH_KEYS: Record<string, string> = {
|
const DEFAULT_INSTRUCTIONS_PATH_KEYS: Record<string, string> = {
|
||||||
claude_local: "instructionsFilePath",
|
claude_local: "instructionsFilePath",
|
||||||
codex_local: "instructionsFilePath",
|
codex_local: "instructionsFilePath",
|
||||||
@@ -83,6 +85,20 @@ export function agentRoutes(db: Db) {
|
|||||||
pi_local: "instructionsFilePath",
|
pi_local: "instructionsFilePath",
|
||||||
};
|
};
|
||||||
const DEFAULT_MANAGED_INSTRUCTIONS_ADAPTER_TYPES = new Set(Object.keys(DEFAULT_INSTRUCTIONS_PATH_KEYS));
|
const DEFAULT_MANAGED_INSTRUCTIONS_ADAPTER_TYPES = new Set(Object.keys(DEFAULT_INSTRUCTIONS_PATH_KEYS));
|
||||||
|
|
||||||
|
/** Check if an adapter supports the managed instructions bundle. */
|
||||||
|
function adapterSupportsInstructionsBundle(adapterType: string): boolean {
|
||||||
|
const adapter = findActiveServerAdapter(adapterType);
|
||||||
|
if (adapter?.supportsInstructionsBundle !== undefined) return adapter.supportsInstructionsBundle;
|
||||||
|
return DEFAULT_MANAGED_INSTRUCTIONS_ADAPTER_TYPES.has(adapterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolve the adapter config key for the instructions file path. */
|
||||||
|
function resolveInstructionsPathKey(adapterType: string): string | null {
|
||||||
|
const adapter = findActiveServerAdapter(adapterType);
|
||||||
|
if (adapter?.instructionsPathKey) return adapter.instructionsPathKey;
|
||||||
|
return DEFAULT_INSTRUCTIONS_PATH_KEYS[adapterType] ?? null;
|
||||||
|
}
|
||||||
const KNOWN_INSTRUCTIONS_PATH_KEYS = new Set(["instructionsFilePath", "agentsMdPath"]);
|
const KNOWN_INSTRUCTIONS_PATH_KEYS = new Set(["instructionsFilePath", "agentsMdPath"]);
|
||||||
const KNOWN_INSTRUCTIONS_BUNDLE_KEYS = [
|
const KNOWN_INSTRUCTIONS_BUNDLE_KEYS = [
|
||||||
"instructionsBundleMode",
|
"instructionsBundleMode",
|
||||||
@@ -557,7 +573,7 @@ export function agentRoutes(db: Db) {
|
|||||||
adapterType: string;
|
adapterType: string;
|
||||||
adapterConfig: unknown;
|
adapterConfig: unknown;
|
||||||
}>(agent: T): Promise<T> {
|
}>(agent: T): Promise<T> {
|
||||||
if (!DEFAULT_MANAGED_INSTRUCTIONS_ADAPTER_TYPES.has(agent.adapterType)) {
|
if (!adapterSupportsInstructionsBundle(agent.adapterType)) {
|
||||||
return agent;
|
return agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +654,9 @@ export function agentRoutes(db: Db) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const ADAPTERS_REQUIRING_MATERIALIZED_RUNTIME_SKILLS = new Set([
|
// Legacy hardcoded set — used as fallback when adapter module does not
|
||||||
|
// declare requiresMaterializedRuntimeSkills explicitly.
|
||||||
|
const LEGACY_MATERIALIZED_SKILLS_SET = new Set([
|
||||||
"cursor",
|
"cursor",
|
||||||
"gemini_local",
|
"gemini_local",
|
||||||
"opencode_local",
|
"opencode_local",
|
||||||
@@ -646,7 +664,11 @@ export function agentRoutes(db: Db) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
function shouldMaterializeRuntimeSkillsForAdapter(adapterType: string) {
|
function shouldMaterializeRuntimeSkillsForAdapter(adapterType: string) {
|
||||||
return ADAPTERS_REQUIRING_MATERIALIZED_RUNTIME_SKILLS.has(adapterType);
|
const adapter = findActiveServerAdapter(adapterType);
|
||||||
|
if (adapter?.requiresMaterializedRuntimeSkills !== undefined) {
|
||||||
|
return adapter.requiresMaterializedRuntimeSkills;
|
||||||
|
}
|
||||||
|
return LEGACY_MATERIALIZED_SKILLS_SET.has(adapterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildRuntimeSkillConfig(
|
async function buildRuntimeSkillConfig(
|
||||||
@@ -1609,7 +1631,7 @@ export function agentRoutes(db: Db) {
|
|||||||
|
|
||||||
const existingAdapterConfig = asRecord(existing.adapterConfig) ?? {};
|
const existingAdapterConfig = asRecord(existing.adapterConfig) ?? {};
|
||||||
const explicitKey = asNonEmptyString(req.body.adapterConfigKey);
|
const explicitKey = asNonEmptyString(req.body.adapterConfigKey);
|
||||||
const defaultKey = DEFAULT_INSTRUCTIONS_PATH_KEYS[existing.adapterType] ?? null;
|
const defaultKey = resolveInstructionsPathKey(existing.adapterType);
|
||||||
const adapterConfigKey = explicitKey ?? defaultKey;
|
const adapterConfigKey = explicitKey ?? defaultKey;
|
||||||
if (!adapterConfigKey) {
|
if (!adapterConfigKey) {
|
||||||
res.status(422).json({
|
res.status(422).json({
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { adaptersApi, type AdapterCapabilities } from "@/api/adapters";
|
||||||
|
import { queryKeys } from "@/lib/queryKeys";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a lookup function that resolves adapter capabilities by type.
|
||||||
|
*
|
||||||
|
* Capabilities are fetched from the server adapter listing API and cached
|
||||||
|
* via react-query. When the data is not yet loaded, the lookup returns
|
||||||
|
* a conservative default (all capabilities false).
|
||||||
|
*/
|
||||||
|
export function useAdapterCapabilities(): (type: string) => AdapterCapabilities {
|
||||||
|
const { data: adapters } = useQuery({
|
||||||
|
queryKey: queryKeys.adapters.all,
|
||||||
|
queryFn: () => adaptersApi.list(),
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const capMap = useMemo(() => {
|
||||||
|
const map = new Map<string, AdapterCapabilities>();
|
||||||
|
if (adapters) {
|
||||||
|
for (const a of adapters) {
|
||||||
|
map.set(a.type, a.capabilities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [adapters]);
|
||||||
|
|
||||||
|
return (type: string): AdapterCapabilities =>
|
||||||
|
capMap.get(type) ?? {
|
||||||
|
supportsInstructionsBundle: false,
|
||||||
|
supportsSkills: false,
|
||||||
|
supportsLocalAgentJwt: false,
|
||||||
|
requiresMaterializedRuntimeSkills: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,6 +4,13 @@
|
|||||||
|
|
||||||
import { api } from "./client";
|
import { api } from "./client";
|
||||||
|
|
||||||
|
export interface AdapterCapabilities {
|
||||||
|
supportsInstructionsBundle: boolean;
|
||||||
|
supportsSkills: boolean;
|
||||||
|
supportsLocalAgentJwt: boolean;
|
||||||
|
requiresMaterializedRuntimeSkills: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AdapterInfo {
|
export interface AdapterInfo {
|
||||||
type: string;
|
type: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -11,6 +18,7 @@ export interface AdapterInfo {
|
|||||||
modelsCount: number;
|
modelsCount: number;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
capabilities: AdapterCapabilities;
|
||||||
/** Installed version (for external npm adapters) */
|
/** Installed version (for external npm adapters) */
|
||||||
version?: string;
|
version?: string;
|
||||||
/** Package name (for external adapters) */
|
/** Package name (for external adapters) */
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import { listAdapterOptions, listVisibleAdapterTypes } from "../adapters/metadat
|
|||||||
import { getAdapterLabel } from "../adapters/adapter-display-registry";
|
import { getAdapterLabel } from "../adapters/adapter-display-registry";
|
||||||
import { useDisabledAdaptersSync } from "../adapters/use-disabled-adapters";
|
import { useDisabledAdaptersSync } from "../adapters/use-disabled-adapters";
|
||||||
import { buildAgentUpdatePatch, type AgentConfigOverlay } from "../lib/agent-config-patch";
|
import { buildAgentUpdatePatch, type AgentConfigOverlay } from "../lib/agent-config-patch";
|
||||||
|
import { useAdapterCapabilities } from "../adapters/use-adapter-capabilities";
|
||||||
|
|
||||||
/* ---- Create mode values ---- */
|
/* ---- Create mode values ---- */
|
||||||
|
|
||||||
@@ -269,8 +270,9 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
|||||||
const adapterType = isCreate
|
const adapterType = isCreate
|
||||||
? props.values.adapterType
|
? props.values.adapterType
|
||||||
: overlay.adapterType ?? props.agent.adapterType;
|
: overlay.adapterType ?? props.agent.adapterType;
|
||||||
const NONLOCAL_TYPES = new Set(["process", "http", "openclaw_gateway"]);
|
const getCapabilities = useAdapterCapabilities();
|
||||||
const isLocal = !NONLOCAL_TYPES.has(adapterType);
|
const adapterCaps = getCapabilities(adapterType);
|
||||||
|
const isLocal = adapterCaps.supportsInstructionsBundle || adapterCaps.supportsSkills || adapterCaps.supportsLocalAgentJwt;
|
||||||
|
|
||||||
const showLegacyWorkingDirectoryField =
|
const showLegacyWorkingDirectoryField =
|
||||||
isLocal && shouldShowLegacyWorkingDirectoryField({ isCreate, adapterConfig: config });
|
isLocal && shouldShowLegacyWorkingDirectoryField({ isCreate, adapterConfig: config });
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
import { getUIAdapter } from "../adapters";
|
import { getUIAdapter } from "../adapters";
|
||||||
import { listUIAdapters } from "../adapters";
|
import { listUIAdapters } from "../adapters";
|
||||||
import { useDisabledAdaptersSync } from "../adapters/use-disabled-adapters";
|
import { useDisabledAdaptersSync } from "../adapters/use-disabled-adapters";
|
||||||
|
import { useAdapterCapabilities } from "../adapters/use-adapter-capabilities";
|
||||||
import { getAdapterDisplay } from "../adapters/adapter-display-registry";
|
import { getAdapterDisplay } from "../adapters/adapter-display-registry";
|
||||||
import { defaultCreateValues } from "./agent-config-defaults";
|
import { defaultCreateValues } from "./agent-config-defaults";
|
||||||
import { parseOnboardingGoalInput } from "../lib/onboarding-goal";
|
import { parseOnboardingGoalInput } from "../lib/onboarding-goal";
|
||||||
@@ -198,8 +199,9 @@ export function OnboardingWizard() {
|
|||||||
queryFn: () => agentsApi.adapterModels(createdCompanyId!, adapterType),
|
queryFn: () => agentsApi.adapterModels(createdCompanyId!, adapterType),
|
||||||
enabled: Boolean(createdCompanyId) && effectiveOnboardingOpen && step === 2
|
enabled: Boolean(createdCompanyId) && effectiveOnboardingOpen && step === 2
|
||||||
});
|
});
|
||||||
const NONLOCAL_TYPES = new Set(["process", "http", "openclaw_gateway"]);
|
const getCapabilities = useAdapterCapabilities();
|
||||||
const isLocalAdapter = !NONLOCAL_TYPES.has(adapterType);
|
const adapterCaps = getCapabilities(adapterType);
|
||||||
|
const isLocalAdapter = adapterCaps.supportsInstructionsBundle || adapterCaps.supportsSkills || adapterCaps.supportsLocalAgentJwt;
|
||||||
|
|
||||||
// Build adapter grids dynamically from the UI registry + display metadata.
|
// Build adapter grids dynamically from the UI registry + display metadata.
|
||||||
// External/plugin adapters automatically appear with generic defaults.
|
// External/plugin adapters automatically appear with generic defaults.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { AgentConfigForm } from "../components/AgentConfigForm";
|
|||||||
import { PageTabBar } from "../components/PageTabBar";
|
import { PageTabBar } from "../components/PageTabBar";
|
||||||
import { adapterLabels, roleLabels, help } from "../components/agent-config-primitives";
|
import { adapterLabels, roleLabels, help } from "../components/agent-config-primitives";
|
||||||
import { ToggleSwitch } from "@/components/ui/toggle-switch";
|
import { ToggleSwitch } from "@/components/ui/toggle-switch";
|
||||||
|
import { useAdapterCapabilities } from "@/adapters/use-adapter-capabilities";
|
||||||
import { MarkdownEditor } from "../components/MarkdownEditor";
|
import { MarkdownEditor } from "../components/MarkdownEditor";
|
||||||
import { assetsApi } from "../api/assets";
|
import { assetsApi } from "../api/assets";
|
||||||
import { getUIAdapter, buildTranscript, onAdapterChange } from "../adapters";
|
import { getUIAdapter, buildTranscript, onAdapterChange } from "../adapters";
|
||||||
@@ -1719,13 +1720,8 @@ function PromptsTab({
|
|||||||
externalBundleRef.current = null;
|
externalBundleRef.current = null;
|
||||||
}, [agent.id]);
|
}, [agent.id]);
|
||||||
|
|
||||||
const isLocal =
|
const getCapabilities = useAdapterCapabilities();
|
||||||
agent.adapterType === "claude_local" ||
|
const isLocal = getCapabilities(agent.adapterType).supportsInstructionsBundle;
|
||||||
agent.adapterType === "codex_local" ||
|
|
||||||
agent.adapterType === "opencode_local" ||
|
|
||||||
agent.adapterType === "pi_local" ||
|
|
||||||
agent.adapterType === "hermes_local" ||
|
|
||||||
agent.adapterType === "cursor";
|
|
||||||
|
|
||||||
const { data: bundle, isLoading: bundleLoading } = useQuery({
|
const { data: bundle, isLoading: bundleLoading } = useQuery({
|
||||||
queryKey: queryKeys.agents.instructionsBundle(agent.id),
|
queryKey: queryKeys.agents.instructionsBundle(agent.id),
|
||||||
|
|||||||
Reference in New Issue
Block a user