mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 16:27:18 +02:00
feat(adapters): refresh current coding models and reasoning controls (#13829)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Its adapters supply model catalogs and reasoning controls to agent setup. > - Several provider releases are missing from the fallback catalogs. > - Some newer models also have effort levels that the UI does not offer. > - Operators need the exact supported IDs and controls when discovery is unavailable. > - This pull request updates the existing adapters from current provider documentation. > - Operators can select current coding models without entering custom IDs. ## Linked Issues or Issue Description **What existing behavior does this improve?** Model selection and reasoning controls across the existing coding-agent adapters. **Subsystem affected** Claude, Codex, Grok, Gemini, Cursor, Kimi, and OpenCode adapters; model discovery tests; agent creation and editing. **Current behavior** The catalogs omit Opus 5.5, GPT-6 Sol/Luna, Grok 4.7/4.6/4.5, current Gemini Flash models, and several Cursor/Kimi choices. Bedrock has obsolete IDs. The UI omits supported effort levels and saves Grok effort under a key the runtime does not read. **Proposed behavior** Offer verified current model IDs and model-specific efforts. Remove retired Gemini 2.0 choices. Keep configured defaults and saved model IDs. Keep runtime discovery for account-specific choices. **Reason and benefit** Catch up with provider releases through September 22, 2026. Correct the picker and runtime controls together. **Breaking changes** No database or API change. Gemini 2.0 options leave the picker after their June 1 shutdown. Existing saved IDs remain unchanged. Corrected Bedrock catalog IDs do not rewrite saved configuration. **Additional context** Supersedes the separate GPT-6 Sol PR #13830. Fable 5.1 was already merged in #12730, and GPT-6 Astra in #12851. The Grok 4.6/4.5 proposal #11324 was closed and parked by its author. This change retains the default-sentinel fix from #12062. Related discovery proposals #13127 and #13565 do not supply these catalog and effort updates. Searches found no open PR for the additional model IDs. See [the dated audit](https://github.com/paperclipai/paperclip/blob/feat/claude-opus-5-5/doc/adapter-model-audit-2026-09-22.md) for exact scope, primary sources, runtime observations, and account-specific limits. This updates existing adapters and does not duplicate planned core work. ## What Changed - Add Opus 5.5 for direct Claude and Bedrock, with a Claude Code 2.1.280 gate. Correct and extend Bedrock model IDs. - Add GPT-6 Sol/Luna and Fast mode. Offer Ultra for Astra/Sol and GPT-5.6 Sol/Terra, and Max for both Luna generations. - Add Grok 4.7/4.6/4.5, expose supported Extra High effort, and save Grok edits under `reasoningEffort`. - Add Gemini Flash 3.8/3.7/3.6/3.5, Flash Lite 3.5/3.1, and 3 Flash Preview. Remove retired 2.0 choices. - Add the current documented Cursor fallback models, including Fable 5.1, Composer 2.5, and Muse Spark 1.3. - Refresh OpenCode fallback IDs used in remote environments from its installed provider registry. - Add Kimi K3 256K. Update the existing coding alias to K2.8 Preview and enable its CLI effort settings. - Use model-specific Claude/Grok efforts in creation and editing. Clear unsupported effort when switching models. - Add catalog, CLI/ACP forwarding, compatibility, and UI persistence coverage. Record the audit and sources. ## Verification - Latest head `6e63c9ef53b54ba869cd4fb431a8570bebe289f4`: 53 CI checks passed, 2 skipped. This includes full workspace typecheck, build, and all test shards. Greptile is 5/5 with zero unresolved threads. GitHub reports no merge conflicts. - 340 focused tests passed across adapter metadata, CLI/ACP arguments, Claude version checks, Kimi effort, Grok execution, server model discovery, and UI effort selection/persistence. - `pnpm --filter @paperclipai/adapter-claude-local --filter @paperclipai/adapter-codex-local --filter @paperclipai/adapter-grok-local --filter @paperclipai/adapter-gemini-local --filter @paperclipai/adapter-kimi-local --filter @paperclipai/adapter-cursor-local --filter @paperclipai/adapter-opencode-local typecheck` — passed. The same filters with `build` passed. - `pnpm check:token-gates` and `git diff --check` — passed. - Full workspace and UI typechecks were attempted locally. They stop on existing missing `three` dependencies in `packages/shared/src/cliplab`. - Full `pnpm test:run` and `pnpm build` were not run locally. Worktree creation exhausted disk space, so a clean dependency install is not feasible on this host. Focused checks reuse existing dependencies. CI supplies full workspace verification. - No provider inference was run. Account-specific runtime model lists were inspected where available. - Manual check: select the new models in agent setup and editing. Confirm Luna has Max but no Ultra, Grok 4.7 has Extra High, and Fable 5.1 has Extra High/Max. Save Grok effort and confirm `adapterConfig.reasoningEffort` contains the selection. ## Risks - Catalog presence does not grant account access. Older CLIs and restricted accounts can reject a model. Opus 5.5 has an explicit upgrade check. - Higher effort can increase cost and latency. Existing agent defaults are unchanged. - Cursor fallback IDs come from public model documentation; the local account exposed no live catalog. Runtime discovery still adds account-specific variants. - Kimi effort remains supported only on its explicit CLI engine. This does not add effort support to its default ACP engine. - Saved obsolete Bedrock or retired Gemini IDs are not migrated automatically. ## Model Used OpenAI GPT-6 through Codex, with reasoning, tool use, and code execution. The exact deployment ID and context window are not exposed to this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
7badae6981
commit
cdf04a33fa
29 files changed
+379
-86
No files matched your search
@@ -71,6 +71,7 @@ describe("adapter model listing", () => {
|
||||
expect(models.some((model) => model.id === "claude-mythos-5")).toBe(true);
|
||||
// Opus 5 is a current GA flagship and must be offered even when live discovery is unavailable.
|
||||
expect(models.some((model) => model.id === "claude-opus-5")).toBe(true);
|
||||
expect(models).toContainEqual({ id: "claude-opus-5-5", label: "Claude Opus 5.5" });
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -93,6 +94,7 @@ describe("adapter model listing", () => {
|
||||
expect(first).toEqual(second);
|
||||
expect(first.some((model) => model.id === "claude-opus-4-8-20260529")).toBe(true);
|
||||
expect(first.some((model) => model.id === "claude-opus-4-8")).toBe(true);
|
||||
expect(first.some((model) => model.id === "claude-opus-5-5")).toBe(true);
|
||||
});
|
||||
|
||||
it("refreshes cached claude models on demand", async () => {
|
||||
@@ -131,18 +133,21 @@ describe("adapter model listing", () => {
|
||||
expect(models).toEqual(claudeFallbackModels);
|
||||
});
|
||||
|
||||
it("does not duplicate claude-fable-5-1 when discovery returns the identical ID", async () => {
|
||||
it.each([
|
||||
["claude-fable-5-1", "Claude Fable 5.1"],
|
||||
["claude-opus-5-5", "Claude Opus 5.5"],
|
||||
])("does not duplicate %s when discovery returns the identical ID", async (id, displayName) => {
|
||||
process.env.ANTHROPIC_API_KEY = "sk-ant-test";
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: [{ id: "claude-fable-5-1", display_name: "Claude Fable 5.1" }],
|
||||
data: [{ id, display_name: displayName }],
|
||||
}),
|
||||
} as Response);
|
||||
|
||||
const models = await listAdapterModels("claude_local");
|
||||
|
||||
expect(models.filter((model) => model.id === "claude-fable-5-1")).toHaveLength(1);
|
||||
expect(models.filter((model) => model.id === id)).toEqual([{ id, label: displayName }]);
|
||||
// Curated fallbacks discovery did not return are still merged in.
|
||||
expect(models.some((model) => model.id === "claude-fable-5")).toBe(true);
|
||||
expect(models.some((model) => model.id === "claude-opus-4-8")).toBe(true);
|
||||
@@ -154,13 +159,42 @@ describe("adapter model listing", () => {
|
||||
|
||||
const models = await listAdapterModels("claude_local");
|
||||
|
||||
// The Bedrock default (first entry) is unchanged.
|
||||
expect(models[0]?.id).toBe("us.anthropic.claude-opus-4-8-v1");
|
||||
expect(models.some((model) => model.id === "us.anthropic.claude-fable-5-1")).toBe(true);
|
||||
// Keep Opus 4.8 first, using its documented dateless Bedrock ID.
|
||||
expect(models[0]?.id).toBe("us.anthropic.claude-opus-4-8");
|
||||
expect(models.map((model) => model.id)).toEqual(expect.arrayContaining([
|
||||
"us.anthropic.claude-opus-5-5", "us.anthropic.claude-opus-5", "us.anthropic.claude-sonnet-5",
|
||||
"us.anthropic.claude-fable-5-1", "us.anthropic.claude-opus-4-7", "us.anthropic.claude-sonnet-4-6",
|
||||
]));
|
||||
expect(models.map((model) => model.id)).not.toEqual(expect.arrayContaining(["us.anthropic.claude-opus-4-8-v1"]));
|
||||
expect(models.some((model) => model.id === "claude-fable-5-1")).toBe(false);
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["gemini_local", ["gemini-3.8-flash", "gemini-3.7-flash", "gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.1-flash-lite", "gemini-3-flash-preview"]],
|
||||
["grok_local", ["grok-build", "grok-4.7", "grok-4.6", "grok-4.5"]],
|
||||
["kimi_local", ["kimi-code/kimi-for-coding", "kimi-code/k3", "kimi-code/k3-256k"]],
|
||||
])("lists current %s models without a provider login", async (adapter, expectedIds) => {
|
||||
const models = await listAdapterModels(adapter as string);
|
||||
expect(models.map((model) => model.id)).toEqual(expect.arrayContaining(expectedIds as string[]));
|
||||
expect(new Set(models.map((model) => model.id)).size).toBe(models.length);
|
||||
if (adapter === "gemini_local") {
|
||||
expect(models.some((model) => model.id.startsWith("gemini-2.0-"))).toBe(false);
|
||||
}
|
||||
if (adapter === "kimi_local") {
|
||||
expect(models).toContainEqual({ id: "kimi-code/kimi-for-coding", label: "K2.8 Preview" });
|
||||
}
|
||||
});
|
||||
|
||||
it("includes current Cursor fallbacks when runtime discovery is unavailable", async () => {
|
||||
setCursorModelsRunnerForTests(() => ({ status: 1, stdout: "", stderr: "", hasError: true }));
|
||||
const models = await listAdapterModels("cursor");
|
||||
expect(models.map((model) => model.id)).toEqual(expect.arrayContaining([
|
||||
"composer-2.5", "claude-opus-5-5", "claude-fable-5-1", "claude-sonnet-5",
|
||||
"gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "grok-4.7", "gemini-3.8-flash", "muse-spark-1.3",
|
||||
]));
|
||||
});
|
||||
|
||||
it("loads codex models dynamically and merges fallback options", async () => {
|
||||
process.env.OPENAI_API_KEY = "sk-test";
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue({
|
||||
@@ -232,12 +266,13 @@ describe("adapter model listing", () => {
|
||||
expect(models).toEqual(cursorFallbackModels);
|
||||
});
|
||||
|
||||
it("returns opencode fallback models including gpt-5.4", async () => {
|
||||
it("returns current provider-qualified OpenCode models when discovery is unavailable", async () => {
|
||||
process.env.PAPERCLIP_OPENCODE_COMMAND = "__paperclip_missing_opencode_command__";
|
||||
|
||||
const models = await listAdapterModels("opencode_local");
|
||||
|
||||
expect(models).toEqual(opencodeFallbackModels);
|
||||
expect(models.map((model) => model.id)).toEqual(expect.arrayContaining(["openai/gpt-6-astra", "openai/gpt-6-sol", "openai/gpt-6-luna", "openai/gpt-5.6-sol", "openai/gpt-5.6-terra", "openai/gpt-5.6-luna", "anthropic/claude-opus-5-5", "anthropic/claude-opus-5", "anthropic/claude-fable-5-1", "anthropic/claude-sonnet-5", "google/gemini-3.8-flash", "xai/grok-4.7"]));
|
||||
});
|
||||
|
||||
it("loads cursor models dynamically and caches them", async () => {
|
||||
|
||||
Reference in new issue
Block a user