feat: default Claude to the native runner on Apple Silicon

Carry Claude effort through versioned native admission and recovery. Preserve existing connections and saved runner choices, and price Opus 5.5 API receipts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-12 10:48:21 -05:00
1 parent 058af8f1b6
commit 6fd56c445d
77 files changed
+909 -296

No files matched your search

+32 -29
View File
@@ -1,7 +1,7 @@
import { useConnectionModels } from "./ai-connections/useConnectionModels";
import { agentHarnessType, agentRunner, type AgentRunnerChoice } from "@paperclipai/shared";
import { adaptersApi } from "../api/adapters";
import { CodexRunnerSelect } from "./CodexRunnerSelect";
import { AgentRunnerSelect } from "./AgentRunnerSelect";
import { aiRoutingHarness } from "@paperclipai/shared";
import { AiConnectionField } from "./ai-connections/AiConnectionField";
import { aiConnectionBindingSchema, aiRuntimeConnectionBindingSchema } from "@paperclipai/shared";
@@ -609,6 +609,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
const effectiveConfig = isCreate ? props.values.adapterSchemaValues ?? {} : { ...config, ...overlay.adapterConfig };
const harnessType = agentHarnessType(adapterType, effectiveConfig);
const isCodexHarness = harnessType === "codex_local";
const isRunnerHarness = isCodexHarness || harnessType === "claude_local";
const rawCurrentDefaultEnvironmentId = isCreate
? props.values.defaultEnvironmentId ?? ""
: eff("identity", "defaultEnvironmentId", props.agent.defaultEnvironmentId ?? "");
@@ -622,13 +623,13 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
queryFn: () => adaptersApi.list({ companyId: selectedCompanyId!, environmentId: rawCurrentDefaultEnvironmentId || null }),
enabled: Boolean(selectedCompanyId),
});
const codexRunnerAvailability = runnerDiscovery.data?.find(item => item.type === "codex_local");
const runnerAvailability = runnerDiscovery.data?.find(item => item.type === harnessType);
const runner: AgentRunnerChoice = isCreate ? props.values.runner ?? "auto" : overlay.runner ?? agentRunner(adapterType);
const fieldsAdapterType = isCodexHarness
? (runner === "paperclip" || (runner === "auto" && codexRunnerAvailability?.defaultRunner === "paperclip") ? "paperclip_runner" : "codex_local")
const fieldsAdapterType = isRunnerHarness
? (runner === "paperclip" || (runner === "auto" && runnerAvailability?.defaultRunner === "paperclip") ? "paperclip_runner" : harnessType)
: adapterType;
const getCapabilities = useAdapterCapabilities();
const adapterCaps = getCapabilities(isCodexHarness ? harnessType : adapterType);
const adapterCaps = getCapabilities(isRunnerHarness ? harnessType : adapterType);
const isDotRunner = adapterType === "paperclip_runner" && (isCreate ? props.values.adapterSchemaValues?.provider : eff("adapterConfig", "provider", config.provider)) === "openai_dot";
const isLocal = !isDotRunner && (adapterCaps.supportsInstructionsBundle || adapterCaps.supportsSkills || adapterCaps.supportsLocalAgentJwt);
@@ -918,7 +919,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
).data?.provider === "openrouter" ? "openrouter" : runnerProvider;
const connectionModels = useConnectionModels(selectedCompanyId, isCreate ? undefined : aiConnectionBindingSchema.safeParse((overlay.runtime.runtimeConfig as Record<string, unknown> | undefined)?.aiConnection ?? runtimeConfig.aiConnection).data, aiRoutingHarness(adapterType, runnerProvider, eff("adapterConfig", "acpxAgent", config.acpxAgent)));
// Fetch adapter models for the effective provider, including unsaved changes.
const modelAdapterType = isCodexHarness ? harnessType : adapterType;
const modelAdapterType = isRunnerHarness ? harnessType : adapterType;
const modelQueryKey = selectedCompanyId
? queryKeys.agents.adapterModels(selectedCompanyId, modelAdapterType, currentDefaultEnvironmentId || null, modelProvider)
: ["agents", "none", "adapter-models", adapterType];
@@ -970,10 +971,10 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
mode,
isCreate,
adapterType: fieldsAdapterType,
hideRunnerHarness: isCodexHarness,
values: isCreate ? props.values : null,
hideRunnerHarness: isRunnerHarness,
values: isCreate ? { ...props.values, ...(harnessType === "claude_local" && fieldsAdapterType === "paperclip_runner" ? { adapterSchemaValues: { ...props.values.adapterSchemaValues, provider: "acpx", acpxAgent: "claude" } } : {}) } : null,
set: isCreate ? (patch: Partial<CreateConfigValues>) => props.onChange(patch) : null,
config,
config: harnessType === "claude_local" && fieldsAdapterType === "paperclip_runner" ? { ...config, provider: "acpx", acpxAgent: "claude" } : config,
eff: eff as <T>(group: "adapterConfig", field: string, original: T) => T,
// Harness transitions supply the new harness's default model. Resolve
// user-selected model IDs in ModelDropdown, not through the previous
@@ -1104,9 +1105,9 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
: adapterConfig.provider === "acpx" && adapterConfig.acpxAgent === "claude" ? "claude_local"
: adapterType
: adapterType;
return testAgentSetup({ companyId: selectedCompanyId, adapterType, providerAdapter, adapterConfig, agentId, aiConnection, environmentId, ...(isCodexHarness && (isCreate ? runner !== "auto" : overlay.runner !== undefined) ? { runner } : {}) });
return testAgentSetup({ companyId: selectedCompanyId, adapterType, providerAdapter, adapterConfig, agentId, aiConnection, environmentId, ...(isRunnerHarness && (isCreate ? runner !== "auto" : overlay.runner !== undefined) ? { runner } : {}) });
}
return agentsApi.testEnvironment(selectedCompanyId, adapterType, { adapterConfig, agentId, aiConnection, environmentId, ...(isCodexHarness && (isCreate ? runner !== "auto" : overlay.runner !== undefined) ? { runner } : {}) });
return agentsApi.testEnvironment(selectedCompanyId, adapterType, { adapterConfig, agentId, aiConnection, environmentId, ...(isRunnerHarness && (isCreate ? runner !== "auto" : overlay.runner !== undefined) ? { runner } : {}) });
},
});
const [testActionPending, setTestActionPending] = useState(false);
@@ -1153,7 +1154,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
resetTestEnvironmentRef.current();
setTestActionError(null);
clearClaudeLoginClaimRef.current();
}, [adapterType, runner, effectiveLoginEnvironmentId, runner === "auto" ? codexRunnerAvailability?.defaultRunner : undefined]);
}, [adapterType, runner, effectiveLoginEnvironmentId, runner === "auto" ? runnerAvailability?.defaultRunner : undefined]);
// Show the login affordance only for a current sandbox adapter that declares a
// login capability, and whose most recent Test result carries the canonical
@@ -1164,7 +1165,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
const adapterSupportsSandboxLogin = adapterCaps.login != null;
const testResult = testEnvironment.data;
const testResultSupportsSandboxLogin =
testResult != null && getCapabilities(isCodexHarness ? harnessType : testResult.adapterType).login != null;
testResult != null && getCapabilities(isRunnerHarness ? harnessType : testResult.adapterType).login != null;
const authMissingCheck =
testResult && testResultSupportsSandboxLogin
? testResult.checks.find((check) => check.code === ADAPTER_AUTH_MISSING_CHECK_CODE) ?? null
@@ -1259,7 +1260,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
// environment id, so both are present here.
login:
showAdapterLogin && selectedCompanyId && effectiveLoginEnvironmentId
? { companyId: selectedCompanyId, adapterType: isCodexHarness ? harnessType : adapterType, environmentId: effectiveLoginEnvironmentId }
? { companyId: selectedCompanyId, adapterType: isRunnerHarness ? harnessType : adapterType, environmentId: effectiveLoginEnvironmentId }
: null,
});
return () => {
@@ -1297,7 +1298,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
}
const thinkingEffortKey =
isCodexHarness
isCodexHarness || (adapterType === "paperclip_runner" && harnessType === "claude_local")
? "modelReasoningEffort"
: adapterType === "cursor"
? "mode"
@@ -1319,19 +1320,19 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
? kimiThinkingEffortOptions
: adapterType === "pi_local"
? [{ id: "", label: "Auto" }, ...["off", "minimal", "low", "medium", "high", "xhigh"].map(id => ({ id, label: id }))]
: adapterType === "claude_local" || adapterType === "grok_local"
? [{ id: "", label: "Auto" }, ...setupEfforts(adapterType, currentModelId).map((id) => ({
: harnessType === "claude_local" || adapterType === "grok_local"
? [{ id: "", label: "Auto" }, ...setupEfforts(harnessType, currentModelId).map((id) => ({
id,
label: id === "xhigh" ? "X-High" : id[0].toUpperCase() + id.slice(1),
}))]
: claudeThinkingEffortOptions;
const currentThinkingEffort = isCreate
? val!.thinkingEffort
: isCodexHarness
: isCodexHarness || (adapterType === "paperclip_runner" && harnessType === "claude_local")
? eff(
"adapterConfig",
thinkingEffortKey,
String(config[thinkingEffortKey] ?? config.reasoningEffort ?? config.modelReasoningEffort ?? ""),
String(config[thinkingEffortKey] ?? config.reasoningEffort ?? config.modelReasoningEffort ?? config.effort ?? ""),
)
: adapterType === "cursor"
? eff("adapterConfig", "mode", String(config.mode ?? ""))
@@ -1340,7 +1341,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
: eff("adapterConfig", thinkingEffortKey, String(config[thinkingEffortKey] ?? ""));
const showThinkingEffort = adapterType !== "gemini_local"
&& adapterType !== "cursor_cloud"
&& (adapterType !== "paperclip_runner" || isCodexHarness);
&& (adapterType !== "paperclip_runner" || isRunnerHarness);
const codexSearchEnabled = adapterType === "codex_local"
? (isCreate ? Boolean(val!.search) : eff("adapterConfig", "search", Boolean(config.search)))
: false;
@@ -1628,7 +1629,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
{showAdapterTypeField && (
<Field label="Harness" hint={help.adapterType}>
<AdapterTypeDropdown
value={isDotRunner ? "openai_dot" : isCodexHarness ? harnessType : adapterType}
value={isDotRunner ? "openai_dot" : isRunnerHarness ? harnessType : adapterType}
disabledTypes={adapterPickerDisabledTypes}
openAiDotEnabled={experimentalSettings?.enableOpenAiDot === true}
nativeRunnerEnabled={experimentalSettings?.enableNativeRunner === true}
@@ -1664,7 +1665,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
setOverlay((prev) => ({
...prev,
adapterType: t,
runner: t === "codex_local" ? "auto" : undefined,
runner: ["codex_local", "claude_local"].includes(t) ? "auto" : undefined,
adapterConfig: {
model:
t === "gemini_local"
@@ -1720,7 +1721,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
<AdapterLoginPanel
key={`${adapterType}:${effectiveLoginEnvironmentId}`}
companyId={selectedCompanyId!}
adapterType={isCodexHarness ? harnessType : adapterType}
adapterType={isRunnerHarness ? harnessType : adapterType}
environmentId={effectiveLoginEnvironmentId!}
onStored={isCreate ? handleClaudeLoginStored : handleClaudeLoginStoredEdit}
onApplyStored={
@@ -1762,8 +1763,8 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
loadingModels={connectionModels?.isLoading ?? fetchingModels}
value={currentModelId}
onChange={(v) => {
const supportedEfforts = setupEfforts(isCodexHarness ? harnessType : adapterType, v);
const clearUnsupportedEffort = ["codex_local", "claude_local", "grok_local"].includes(isCodexHarness ? harnessType : adapterType)
const supportedEfforts = setupEfforts(isRunnerHarness ? harnessType : adapterType, v);
const clearUnsupportedEffort = ["codex_local", "claude_local", "grok_local"].includes(isRunnerHarness ? harnessType : adapterType)
&& Boolean(currentThinkingEffort)
&& !supportedEfforts.includes(String(currentThinkingEffort));
if (isCreate) {
@@ -1831,7 +1832,9 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
? set!({ thinkingEffort: v })
: (() => {
mark("adapterConfig", thinkingEffortKey, v || undefined);
if (isCodexHarness) mark("adapterConfig", thinkingEffortKey === "reasoningEffort" ? "modelReasoningEffort" : "reasoningEffort", undefined);
if (isRunnerHarness) {
for (const key of ["modelReasoningEffort", "reasoningEffort", "effort"]) if (key !== thinkingEffortKey) mark("adapterConfig", key, undefined);
}
})()
}
open={thinkingEffortOpen}
@@ -1893,10 +1896,10 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
onToggle={() => setConfigurationAdvancedOpen(!configurationAdvancedOpen)}
>
<div className="space-y-3">
{isCodexHarness && <CodexRunnerSelect value={runner}
{isRunnerHarness && <AgentRunnerSelect value={runner}
pending={runnerDiscovery.isFetching} error={runnerDiscovery.error?.message} onRetry={() => { void runnerDiscovery.refetch(); }}
defaultRunner={codexRunnerAvailability?.defaultRunner}
supportedRunners={codexRunnerAvailability?.supportedRunners}
defaultRunner={runnerAvailability?.defaultRunner}
supportedRunners={runnerAvailability?.supportedRunners}
onChange={next => isCreate ? set!({ runner: next }) : setOverlay(prev => ({ ...prev, runner: next }))} />}
{isLocal && (<> {/*
The command names a binary on the execution host, so the