Align native setup isolation and finish shared runtime pickers

Use the server-resolved sandbox transport for native setup, retaining local and SSH isolation and stripping injected flags. Preserve the complete local and managed E2E roster and existing graders. Replace the remaining scoped native dropdowns with the shared picker without changing saved runner or account choices.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-13 10:59:57 -05:00
1 parent 448db859e3
commit 30bc9e40ce
17 files changed
+299 -112

No files matched your search

@@ -576,16 +576,20 @@ async function renderStatefulCreateClaudeSandbox(environments: Environment[]) {
return { container, root, valuesRef };
}
async function environmentOptions(container: HTMLElement) {
const picker = container.querySelector('[aria-label="Environment override"]');
expect(picker).not.toBeNull();
await clickElement(picker);
const options = document.body.querySelector('[role="listbox"][aria-label="Environment override"]');
expect(options).not.toBeNull();
return options!;
}
async function selectEnvironment(container: HTMLElement, environmentId: string) {
const select = container.querySelector("select");
await act(async () => {
if (select) {
const setter = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, "value")?.set;
setter?.call(select, environmentId);
select.dispatchEvent(new Event("change", { bubbles: true }));
}
});
await flushReact();
const options = await environmentOptions(container);
const option = options.querySelector(`[role="option"][data-value="${environmentId}"]`);
expect(option).not.toBeNull();
await clickElement(option);
}
async function clickByText(container: HTMLElement, label: string) {
@@ -674,6 +678,40 @@ describe("AgentConfigForm environment selector", () => {
expect(result.onSave).toHaveBeenCalledWith({ name: "Renamed Cody" });
});
it.each(["codex_local", "paperclip_runner"])("changes and clears the environment without changing a saved %s runner or account", async adapterType => {
const adapterConfig = {
model: "gpt-5.6-sol",
...(adapterType === "paperclip_runner" ? { provider: "codex", reasoningEffort: "high" } : { modelReasoningEffort: "high" }),
env: { CODEX_HOME: { type: "secret_ref", secretId: "codex-account" } },
};
const result = await renderForm([
...daytonaEnvironments(),
makeEnvironment({ id: "sandbox-2", name: "Daytona Two", driver: "sandbox", config: { provider: "daytona" } }),
], { adapterType, adapterConfig, defaultEnvironmentId: "sandbox-1" }, { showAdapterTypeField: true });
roots.push(result.root);
await clickByText(result.container, "Advanced");
const runnerLabel = adapterType === "paperclip_runner" ? "Paperclip Runner" : "Legacy runner";
await selectEnvironment(result.container, "sandbox-2");
expect(result.container.querySelector('[aria-label="Environment override"]')?.textContent).toContain("Daytona Two");
expect(result.container.querySelector('[aria-label="Runner"]')?.textContent).toContain(runnerLabel);
await clickByText(result.container, "Save");
expect(result.onSave).toHaveBeenLastCalledWith({ defaultEnvironmentId: "sandbox-2" });
await selectEnvironment(result.container, "");
expect(result.container.querySelector('[aria-label="Environment override"]')?.textContent).toContain("Default: Local");
expect(result.container.querySelector('[aria-label="Runner"]')?.textContent).toContain(runnerLabel);
await clickByText(result.container, "Save");
expect(result.onSave).toHaveBeenLastCalledWith({ defaultEnvironmentId: null });
});
it("clears a new agent's environment override to the empty draft value", async () => {
const result = await renderCreateForm(daytonaEnvironments(), { defaultEnvironmentId: "sandbox-1", runner: "legacy" });
roots.push(result.root);
await selectEnvironment(result.container, "");
expect(result.onChange).toHaveBeenLastCalledWith({ defaultEnvironmentId: "" });
});
it("submits only an explicit runner change and retains saved configuration for server translation", async () => {
const result = await renderForm([], { adapterType: "paperclip_runner", adapterConfig: { provider: "codex", model: "gpt-5.6-sol", reasoningEffort: "high", env: { CODEX_HOME: { type: "secret_ref", secretId: "codex-account" } } } });
roots.push(result.root);
@@ -910,7 +948,7 @@ describe("AgentConfigForm environment selector", () => {
roots.push(result.root);
expect(result.container.textContent).not.toContain("Environment override");
expect(result.container.querySelector("select")).toBeNull();
expect(result.container.querySelector('[aria-label="Environment override"]')).toBeNull();
});
it("renders GPT-6 Astra and its model-specific reasoning efforts", async () => {
@@ -1029,7 +1067,7 @@ describe("AgentConfigForm environment selector", () => {
roots.push(result.root);
const text = result.container.textContent ?? "";
const selector = result.container.querySelector("select");
const selector = await environmentOptions(result.container);
expect(text).toContain("Environment");
expect(text).toContain("Environment override");
@@ -1048,7 +1086,7 @@ describe("AgentConfigForm environment selector", () => {
roots.push(result.root);
const text = result.container.textContent ?? "";
const selector = result.container.querySelector("select");
const selector = await environmentOptions(result.container);
expect(text).toContain("Environment override");
expect(selector?.textContent).toContain("E2B · sandbox");
@@ -1062,7 +1100,7 @@ describe("AgentConfigForm environment selector", () => {
roots.push(result.root);
const text = result.container.textContent ?? "";
const selector = result.container.querySelector("select");
const selector = await environmentOptions(result.container);
expect(text).toContain("Environment override");
expect(selector?.textContent).toContain("E2B · sandbox");
@@ -1084,7 +1122,7 @@ describe("AgentConfigForm environment selector", () => {
roots.push(result.root);
const text = result.container.textContent ?? "";
const selector = result.container.querySelector("select");
const selector = await environmentOptions(result.container);
expect(text).toContain("Environment override");
expect(selector?.textContent).toContain("Default: Local");
@@ -1104,7 +1142,7 @@ describe("AgentConfigForm environment selector", () => {
]);
roots.push(result.root);
const selector = result.container.querySelector("select");
const selector = await environmentOptions(result.container);
expect(selector?.textContent).toContain("Default: Paperclip Computer");
expect(selector?.textContent).toContain("Paperclip Computer");
@@ -2488,15 +2526,7 @@ describe("AgentConfigForm environment selector", () => {
await runTest(result.container);
expect(findButton(result.container, "Sign in")).toBeTruthy();
const select = result.container.querySelector("select");
await act(async () => {
if (select) {
const setter = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, "value")?.set;
setter?.call(select, "");
select.dispatchEvent(new Event("change", { bubbles: true }));
}
});
await flushReact();
await selectEnvironment(result.container, "");
expect(findButton(result.container, "Sign in")).toBeFalsy();
});
+11 -12
View File
@@ -1579,25 +1579,24 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
<div className={cn(cards ? "border border-border rounded-lg p-4 space-y-3" : "px-4 pb-3 space-y-3")}>
<Field label="Environment override">
<div className="space-y-2">
<select
className={inputClass}
<SelectPopover
aria-label="Environment override"
value={currentDefaultEnvironmentId}
onChange={(event) => {
const nextValue = event.target.value;
onValueChange={(nextValue) => {
if (isCreate) {
set!({ defaultEnvironmentId: nextValue });
return;
}
mark("identity", "defaultEnvironmentId", nextValue || null);
}}
>
<option value="">Default: {inheritedEnvironmentLabel}</option>
{environmentOptions.map((environment) => (
<option key={environment.id} value={environment.id}>
{environmentDisplayLabel(environment)}
</option>
))}
</select>
options={[
{ value: "", label: `Default: ${inheritedEnvironmentLabel}` },
...environmentOptions.map((environment) => ({
value: environment.id,
label: environmentDisplayLabel(environment),
})),
]}
/>
</div>
</Field>
</div>