Skip to content

feat: Use instance default adapter for imported agents#1902

Open
davetist wants to merge 5 commits intopaperclipai:masterfrom
davetist:codex/default-instance-import-adapter
Open

feat: Use instance default adapter for imported agents#1902
davetist wants to merge 5 commits intopaperclipai:masterfrom
davetist:codex/default-instance-import-adapter

Conversation

@davetist
Copy link
Copy Markdown

@davetist davetist commented Mar 27, 2026

Thinking Path

  • Paperclip orchestrates AI-agent companies across many adapter types
  • Imported company packages can be vendor-neutral and leave adapter selection to the receiving instance
  • That path was still falling back to Claude even when the instance operator had chosen a different adapter
  • Fresh instances also made the operator pick a default twice because the first created agent did not seed one
  • So vendor-neutral imports need to resolve from an instance-level default adapter, and the first created agent should seed that default automatically
  • This PR adds that instance default, wires new-company imports and the import UI to use it, and removes the temporary Claude fallback path

Summary

  • add an instance-level default adapter setting in General settings using the same adapter selector used in agent configuration
  • use the instance default adapter when importing vendor-neutral process agents into a new company
  • seed the instance default from the first agent ever created when the operator has not explicitly chosen one yet
  • remove the CLI hardcoded claude_local fallback and prefill the import UI from the instance default instead
  • add server, CLI, and UI tests and update the board-operator import docs

Why

Vendor-neutral company imports were still landing on Claude because the import flow relied on a temporary CLI fallback instead of instance configuration.

This also addresses the TODO in cli/src/commands/client/company.ts about replacing the temporary claude_local fallback with adapter selection in the import TUI.

Details

  • server-side import resolution now gives explicit adapter overrides top priority and otherwise maps process agents to the instance default for new_company imports
  • the first created agent now seeds defaultAdapterType, so a fresh instance inherits the connector the operator actually started with without requiring a visit to settings first
  • the adapter selector UI is shared between Agent Configuration and Instance Settings > General so both surfaces expose the same list
  • the company import page now resolves preview defaults from instance settings for new-company imports while preserving explicit user overrides
  • docs now describe the default-adapter behavior for vendor-neutral company imports and the first-agent seeding behavior

Manual Verification

  • set the instance default adapter to codex_local in Instance Settings > General
  • previewed https://github.com/paperclipai/companies/tree/main/product-compass-consulting and confirmed the package imports as vendor-neutral process agents
  • imported that package into a new company through the running local Paperclip server and confirmed the created agents stored adapterType: "codex_local"
  • confirmed the first-agent seeding path preserves an explicitly chosen instance default instead of overwriting it

Automated Verification

  • pnpm test:run
  • pnpm -r typecheck
  • pnpm build

Risks

  • this changes the default resolution for vendor-neutral new-company imports, so any external tooling that still sends explicit adapter overrides will continue to bypass the instance default until that tooling is updated
  • the new instance setting defaults to claude_local when unset, so existing instances keep their current behavior until an operator picks another adapter or the first created agent seeds one

Screenshot

image

@davetist davetist changed the title [codex] Use instance default adapter for imported agents (feat): Use instance default adapter for imported agents Mar 27, 2026
@davetist davetist changed the title (feat): Use instance default adapter for imported agents feat: Use instance default adapter for imported agents Mar 27, 2026
@davetist davetist marked this pull request as ready for review March 27, 2026 10:47
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 27, 2026

Greptile Summary

This PR introduces an instance-level default adapter setting and wires it through the full stack — shared types/validators, server-side import resolution, first-agent seeding, the import UI, and a new InstanceGeneralSettings section — replacing the temporary hardcoded claude_local fallback that vendor-neutral process agents were landing on during new-company imports.

Key changes:

  • packages/shared: defaultAdapterType: AgentAdapterType added to InstanceGeneralSettings type and validator (Zod default "claude_local" preserves backwards compatibility for existing instances).
  • server/services/instance-settings.ts: New seedDefaultAdapterType method uses a raw-property presence check to skip seeding if an operator has already explicitly chosen a default, preventing unintended overwrites.
  • server/services/agents.ts: After an agent insert, a global COUNT(*) check triggers seeding when the total count equals 1 — correctly scoped to the first agent ever created instance-wide.
  • server/services/company-portability.ts: getGeneral().defaultAdapterType is fetched once per new_company import and applied to all process agents; explicit adapterOverrides in the request still take highest priority.
  • ui/src/components/AdapterTypeDropdown.tsx: Shared component extracted from AgentConfigForm; the extraction also picks up a pre-existing bug fix — the popover now closes on selection via controlled state (useState), whereas the old uncontrolled <Popover> left the dropdown open after a choice.
  • ui/src/pages/CompanyImport.tsx: Queries instance settings when targetMode === "new" and only initialises adapter overrides for agents whose resolved type differs from the manifest value, removing the previous blanket CEO-adapter override for all agents.
  • ui/src/pages/InstanceGeneralSettings.tsx: New card section for "Default adapter for imported agents" using the shared AdapterTypeDropdown.
  • Docs: Accurately describes vendor-neutral import resolution, instance-default lookup, and first-agent seeding behaviour.

The PR description includes a detailed thinking path, summary, why/details/verification/risks sections — all requirements are met. However, there are visible UI changes in InstanceGeneralSettings (new section) and CompanyImport (changed adapter pre-fill logic). Please add screenshots of both surfaces to the PR description.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style/UX nits with no runtime impact.

No P0 or P1 issues found. The seeding logic, import resolution priority, and backwards-compatibility defaults are all correctly implemented and backed by new tests. The two P2 findings (missing useCallback/deps for resolveImportAdapterSelection; "process" appearing as Coming Soon in the instance-default dropdown) do not affect correctness or data integrity.

ui/src/components/AdapterTypeDropdown.tsx — consider filtering out "process" from the list when used as an instance-default selector; ui/src/pages/CompanyImport.tsx — resolveImportAdapterSelection should be wrapped in useCallback and added to the useEffect deps array.

Important Files Changed

Filename Overview
server/src/services/agents.ts Adds a post-insert count query (global, no WHERE clause) to detect the first agent ever created instance-wide; calls seedDefaultAdapterType when count === 1. Logic is correct for its stated goal.
server/src/services/instance-settings.ts Adds seedDefaultAdapterType with a raw-property guard (hasExplicitGeneralDefaultAdapterType) to preserve explicit operator choices; double-normalization is harmless. Clean implementation.
server/src/services/company-portability.ts Fetches instance default adapter only for new_company imports and substitutes it for process agents; explicit adapterOverrides remain highest priority. Logic is correct and well-scoped.
ui/src/components/AdapterTypeDropdown.tsx New shared dropdown component extracted from AgentConfigForm; correctly adds controlled open state and closes on selection. Includes "process" and "http" as Coming Soon options, which may be confusing in the instance-default context.
ui/src/pages/CompanyImport.tsx Queries instance settings for new-company imports; only overrides process agents; fixes hardcoded claude_local fallback. useEffect is missing resolveImportAdapterSelection from deps (lint concern, no runtime bug).
ui/src/pages/InstanceGeneralSettings.tsx Adds new "Default adapter for imported agents" section using AdapterTypeDropdown; generalises the single-field mutation to a patch-based mutation cleanly.
packages/shared/src/validators/instance.ts Adds defaultAdapterType with z.enum(AGENT_ADAPTER_TYPES).default("claude_local") — correctly preserves backwards-compatible default for existing instances.

Comments Outside Diff (2)

  1. ui/src/pages/CompanyImport.tsx, line 984-1006 (link)

    P2 resolveImportAdapterSelection missing from useEffect deps

    resolveImportAdapterSelection is called inside the useEffect body but is not listed as a dependency. React's exhaustive-deps rule would flag this.

    In practice this is safe here because resolveImportAdapterSelection captures importProcessDefaultAdapterType, and importProcessDefaultAdapterType for the "new" branch equals instanceGeneralSettingsQuery.data?.defaultAdapterType — which is already in the deps array. However, the omission is fragile: if the function body ever changes to capture another value that isn't in the deps, it will silently stale-close.

    Consider wrapping resolveImportAdapterSelection in useCallback with its real deps so the function reference itself can be tracked:

    const resolveImportAdapterSelection = useCallback(
      (adapterType: string): string => {
        if (adapterType !== "process") return adapterType;
        return importProcessDefaultAdapterType ?? adapterType;
      },
      [importProcessDefaultAdapterType],
    );

    Then include it in the useEffect deps:

    }, [importPreview, targetMode, instanceGeneralSettingsQuery.data?.defaultAdapterType, resolveImportAdapterSelection]);
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: ui/src/pages/CompanyImport.tsx
    Line: 984-1006
    
    Comment:
    **`resolveImportAdapterSelection` missing from `useEffect` deps**
    
    `resolveImportAdapterSelection` is called inside the `useEffect` body but is not listed as a dependency. React's `exhaustive-deps` rule would flag this.
    
    In practice this is safe here because `resolveImportAdapterSelection` captures `importProcessDefaultAdapterType`, and `importProcessDefaultAdapterType` for the `"new"` branch equals `instanceGeneralSettingsQuery.data?.defaultAdapterType` — which *is* already in the deps array. However, the omission is fragile: if the function body ever changes to capture another value that isn't in the deps, it will silently stale-close.
    
    Consider wrapping `resolveImportAdapterSelection` in `useCallback` with its real deps so the function reference itself can be tracked:
    
    ```tsx
    const resolveImportAdapterSelection = useCallback(
      (adapterType: string): string => {
        if (adapterType !== "process") return adapterType;
        return importProcessDefaultAdapterType ?? adapterType;
      },
      [importProcessDefaultAdapterType],
    );
    ```
    
    Then include it in the `useEffect` deps:
    ```tsx
    }, [importPreview, targetMode, instanceGeneralSettingsQuery.data?.defaultAdapterType, resolveImportAdapterSelection]);
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. ui/src/components/AdapterTypeDropdown.tsx, line 727-735 (link)

    P2 "process" appears as a "Coming soon" option in the instance-default dropdown

    AGENT_ADAPTER_TYPES includes "process" and "http", neither of which is in ENABLED_ADAPTER_TYPES, so both show up in the AdapterTypeDropdown as greyed-out "Coming soon" items.

    This is harmless in AgentConfigForm (existing behaviour), but in the new Instance Settings → Default adapter context it is potentially misleading:

    • "process" is a vendor-neutral placeholder, not an adapter coming soon. Labelling it "Coming soon" may puzzle operators who already see process agents in their manifests.
    • If "process" is ever added to ENABLED_ADAPTER_TYPES, an operator could accidentally set it as the instance default, which would leave every vendor-neutral import still as process — silently a no-op.

    Consider excluding "process" (and possibly "http") from the display list when this dropdown is used for instance-level defaults, or at minimum documenting in a comment why they appear disabled.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: ui/src/components/AdapterTypeDropdown.tsx
    Line: 727-735
    
    Comment:
    **`"process"` appears as a "Coming soon" option in the instance-default dropdown**
    
    `AGENT_ADAPTER_TYPES` includes `"process"` and `"http"`, neither of which is in `ENABLED_ADAPTER_TYPES`, so both show up in the `AdapterTypeDropdown` as greyed-out "Coming soon" items.
    
    This is harmless in `AgentConfigForm` (existing behaviour), but in the new **Instance Settings → Default adapter** context it is potentially misleading:
    
    - `"process"` is a vendor-neutral placeholder, not an adapter coming soon. Labelling it "Coming soon" may puzzle operators who already see `process` agents in their manifests.
    - If `"process"` is ever added to `ENABLED_ADAPTER_TYPES`, an operator could accidentally set it as the instance default, which would leave every vendor-neutral import still as `process` — silently a no-op.
    
    Consider excluding `"process"` (and possibly `"http"`) from the display list when this dropdown is used for instance-level defaults, or at minimum documenting in a comment why they appear disabled.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/src/pages/CompanyImport.tsx
Line: 984-1006

Comment:
**`resolveImportAdapterSelection` missing from `useEffect` deps**

`resolveImportAdapterSelection` is called inside the `useEffect` body but is not listed as a dependency. React's `exhaustive-deps` rule would flag this.

In practice this is safe here because `resolveImportAdapterSelection` captures `importProcessDefaultAdapterType`, and `importProcessDefaultAdapterType` for the `"new"` branch equals `instanceGeneralSettingsQuery.data?.defaultAdapterType` — which *is* already in the deps array. However, the omission is fragile: if the function body ever changes to capture another value that isn't in the deps, it will silently stale-close.

Consider wrapping `resolveImportAdapterSelection` in `useCallback` with its real deps so the function reference itself can be tracked:

```tsx
const resolveImportAdapterSelection = useCallback(
  (adapterType: string): string => {
    if (adapterType !== "process") return adapterType;
    return importProcessDefaultAdapterType ?? adapterType;
  },
  [importProcessDefaultAdapterType],
);
```

Then include it in the `useEffect` deps:
```tsx
}, [importPreview, targetMode, instanceGeneralSettingsQuery.data?.defaultAdapterType, resolveImportAdapterSelection]);
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: ui/src/components/AdapterTypeDropdown.tsx
Line: 727-735

Comment:
**`"process"` appears as a "Coming soon" option in the instance-default dropdown**

`AGENT_ADAPTER_TYPES` includes `"process"` and `"http"`, neither of which is in `ENABLED_ADAPTER_TYPES`, so both show up in the `AdapterTypeDropdown` as greyed-out "Coming soon" items.

This is harmless in `AgentConfigForm` (existing behaviour), but in the new **Instance Settings → Default adapter** context it is potentially misleading:

- `"process"` is a vendor-neutral placeholder, not an adapter coming soon. Labelling it "Coming soon" may puzzle operators who already see `process` agents in their manifests.
- If `"process"` is ever added to `ENABLED_ADAPTER_TYPES`, an operator could accidentally set it as the instance default, which would leave every vendor-neutral import still as `process` — silently a no-op.

Consider excluding `"process"` (and possibly `"http"`) from the display list when this dropdown is used for instance-level defaults, or at minimum documenting in a comment why they appear disabled.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Close adapter selector after choosing an..." | Re-trigger Greptile

@davetist
Copy link
Copy Markdown
Author

Addressed the P2 findings and added a screenshot into the PR description.

Imported codex_local agents now receive the same default adapter config as manually created agents, including sandbox bypass when omitted, so imported companies can run Paperclip skills without manual toggles.

Add a shared adapter-defaults helper, cover the import regression with tests, and update the codex adapter docs/spec to match the runtime behavior.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant