mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 13:24:55 +01:00
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:
1 parent
058af8f1b6
commit
6fd56c445d
77 files changed
+909
-296
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user