diff --git a/server/src/__tests__/company-portability.test.ts b/server/src/__tests__/company-portability.test.ts index 58c3af85..67ade31d 100644 --- a/server/src/__tests__/company-portability.test.ts +++ b/server/src/__tests__/company-portability.test.ts @@ -471,4 +471,71 @@ describe("company portability", () => { }, ]); }); + + it("applies adapter overrides while keeping imported AGENTS content implicit", async () => { + const portability = companyPortabilityService({} as any); + + companySvc.create.mockResolvedValue({ + id: "company-imported", + name: "Imported Paperclip", + }); + accessSvc.ensureMembership.mockResolvedValue(undefined); + agentSvc.create.mockResolvedValue({ + id: "agent-created", + name: "ClaudeCoder", + }); + + const exported = await portability.exportBundle("company-1", { + include: { + company: true, + agents: true, + projects: false, + issues: false, + }, + }); + + agentSvc.list.mockResolvedValue([]); + + await portability.importBundle({ + source: { + type: "inline", + rootPath: exported.rootPath, + files: exported.files, + }, + include: { + company: true, + agents: true, + projects: false, + issues: false, + }, + target: { + mode: "new_company", + newCompanyName: "Imported Paperclip", + }, + agents: "all", + collisionStrategy: "rename", + adapterOverrides: { + claudecoder: { + adapterType: "codex_local", + adapterConfig: { + dangerouslyBypassApprovalsAndSandbox: true, + instructionsFilePath: "/tmp/should-not-survive.md", + }, + }, + }, + }, "user-1"); + + expect(agentSvc.create).toHaveBeenCalledWith("company-imported", expect.objectContaining({ + adapterType: "codex_local", + adapterConfig: expect.objectContaining({ + promptTemplate: "You are ClaudeCoder.", + dangerouslyBypassApprovalsAndSandbox: true, + }), + })); + expect(agentSvc.create).toHaveBeenCalledWith("company-imported", expect.objectContaining({ + adapterConfig: expect.not.objectContaining({ + instructionsFilePath: expect.anything(), + }), + })); + }); }); diff --git a/server/src/services/company-portability.ts b/server/src/services/company-portability.ts index 752fa8fc..e5e33d49 100644 --- a/server/src/services/company-portability.ts +++ b/server/src/services/company-portability.ts @@ -2274,7 +2274,7 @@ export function companyPortabilityService(db: Db) { baseAdapterConfig, desiredSkills, ); - delete baseAdapterConfig.instructionsFilePath; + delete adapterConfigWithSkills.instructionsFilePath; const patch = { name: planAgent.plannedName, role: manifestAgent.role, diff --git a/ui/src/adapters/gemini-local/config-fields.tsx b/ui/src/adapters/gemini-local/config-fields.tsx index 050c8d95..7825ea57 100644 --- a/ui/src/adapters/gemini-local/config-fields.tsx +++ b/ui/src/adapters/gemini-local/config-fields.tsx @@ -17,7 +17,9 @@ export function GeminiLocalConfigFields({ config, eff, mark, + hideInstructionsFile, }: AdapterConfigFieldsProps) { + if (hideInstructionsFile) return null; return ( <> diff --git a/ui/src/adapters/index.ts b/ui/src/adapters/index.ts index a4be1438..feb04511 100644 --- a/ui/src/adapters/index.ts +++ b/ui/src/adapters/index.ts @@ -1,4 +1,4 @@ -export { getUIAdapter } from "./registry"; +export { getUIAdapter, listUIAdapters } from "./registry"; export { buildTranscript } from "./transcript"; export type { TranscriptEntry, diff --git a/ui/src/adapters/pi-local/config-fields.tsx b/ui/src/adapters/pi-local/config-fields.tsx index e6afacb3..ad859750 100644 --- a/ui/src/adapters/pi-local/config-fields.tsx +++ b/ui/src/adapters/pi-local/config-fields.tsx @@ -17,7 +17,9 @@ export function PiLocalConfigFields({ config, eff, mark, + hideInstructionsFile, }: AdapterConfigFieldsProps) { + if (hideInstructionsFile) return null; return (
diff --git a/ui/src/adapters/registry.ts b/ui/src/adapters/registry.ts index d8c46738..fc7be2cf 100644 --- a/ui/src/adapters/registry.ts +++ b/ui/src/adapters/registry.ts @@ -9,20 +9,26 @@ import { openClawGatewayUIAdapter } from "./openclaw-gateway"; import { processUIAdapter } from "./process"; import { httpUIAdapter } from "./http"; +const uiAdapters: UIAdapterModule[] = [ + claudeLocalUIAdapter, + codexLocalUIAdapter, + geminiLocalUIAdapter, + openCodeLocalUIAdapter, + piLocalUIAdapter, + cursorLocalUIAdapter, + openClawGatewayUIAdapter, + processUIAdapter, + httpUIAdapter, +]; + const adaptersByType = new Map( - [ - claudeLocalUIAdapter, - codexLocalUIAdapter, - geminiLocalUIAdapter, - openCodeLocalUIAdapter, - piLocalUIAdapter, - cursorLocalUIAdapter, - openClawGatewayUIAdapter, - processUIAdapter, - httpUIAdapter, - ].map((a) => [a.type, a]), + uiAdapters.map((a) => [a.type, a]), ); export function getUIAdapter(type: string): UIAdapterModule { return adaptersByType.get(type) ?? processUIAdapter; } + +export function listUIAdapters(): UIAdapterModule[] { + return [...uiAdapters]; +} diff --git a/ui/src/pages/CompanyImport.tsx b/ui/src/pages/CompanyImport.tsx index a323148b..2f874c05 100644 --- a/ui/src/pages/CompanyImport.tsx +++ b/ui/src/pages/CompanyImport.tsx @@ -26,7 +26,7 @@ import { } from "lucide-react"; import { Field, adapterLabels } from "../components/agent-config-primitives"; import { defaultCreateValues } from "../components/agent-config-defaults"; -import { getUIAdapter } from "../adapters"; +import { getUIAdapter, listUIAdapters } from "../adapters"; import { ClaudeLocalAdvancedFields } from "../adapters/claude-local/config-fields"; import type { CreateConfigValues } from "@paperclipai/adapter-utils"; import { @@ -443,12 +443,10 @@ function ConflictResolutionList({ // ── Adapter type options for import ─────────────────────────────────── -const IMPORT_ADAPTER_OPTIONS: { value: string; label: string }[] = [ - { value: "claude_local", label: adapterLabels.claude_local ?? "Claude (local)" }, - { value: "codex_local", label: adapterLabels.codex_local ?? "Codex (local)" }, - { value: "opencode_local", label: adapterLabels.opencode_local ?? "OpenCode (local)" }, - { value: "cursor", label: adapterLabels.cursor ?? "Cursor (local)" }, -]; +const IMPORT_ADAPTER_OPTIONS: { value: string; label: string }[] = listUIAdapters().map((adapter) => ({ + value: adapter.type, + label: adapterLabels[adapter.type] ?? adapter.label, +})); // ── Adapter picker for imported agents ───────────────────────────────