mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +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
@@ -1,7 +1,21 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { DEFAULT_CLAUDE_LOCAL_MODEL, resolveClaudeModel } from "./index.js";
|
||||
import { claudeLocalReasoningEffortsForModel, DEFAULT_CLAUDE_LOCAL_MODEL, resolveClaudeModel } from "./index.js";
|
||||
import { minimumClaudeCliVersionForModel } from "./server/cli-capabilities.js";
|
||||
|
||||
describe("Claude model defaults", () => {
|
||||
it.each(["claude-opus-5-5", "us.anthropic.claude-opus-5-5", "global.anthropic.claude-opus-5-5[1m]"])("requires a current CLI and offers all efforts for %s", (model) => {
|
||||
expect(minimumClaudeCliVersionForModel(model)).toBe("2.1.280");
|
||||
expect(claudeLocalReasoningEffortsForModel(model)).toEqual(["low", "medium", "high", "xhigh", "max"]);
|
||||
});
|
||||
|
||||
it("keeps model-specific Claude reasoning limits", () => {
|
||||
expect(claudeLocalReasoningEffortsForModel("claude-fable-5-1")).toContain("xhigh");
|
||||
expect(claudeLocalReasoningEffortsForModel("claude-sonnet-5")).toContain("max");
|
||||
expect(claudeLocalReasoningEffortsForModel("claude-sonnet-4-6")).toEqual(["low", "medium", "high", "max"]);
|
||||
expect(claudeLocalReasoningEffortsForModel("claude-haiku-4-5")).toEqual([]);
|
||||
expect(claudeLocalReasoningEffortsForModel("custom-model")).toEqual(["low", "medium", "high"]);
|
||||
});
|
||||
|
||||
it.each([undefined, null, "", " "])("uses Opus 5 for an unset model (%j)", (model) => {
|
||||
expect(DEFAULT_CLAUDE_LOCAL_MODEL).toBe("claude-opus-5");
|
||||
expect(resolveClaudeModel(model)).toBe("claude-opus-5");
|
||||
|
||||
@@ -22,6 +22,16 @@ export function resolveClaudeModel(
|
||||
}
|
||||
|
||||
export const type = "claude_local";
|
||||
|
||||
export function claudeLocalReasoningEffortsForModel(model: string): readonly string[] {
|
||||
const id = model.trim().replace(/\[1m\]$/, "").replace(/^(?:(?:us|eu|apac|global)\.)?anthropic\./, "");
|
||||
if (/^claude-haiku-/.test(id)) return [];
|
||||
if (/^claude-(?:opus-5(?:-5)?|opus-4-[78]|sonnet-5|fable-5(?:-1)?)$/.test(id)) {
|
||||
return ["low", "medium", "high", "xhigh", "max"];
|
||||
}
|
||||
if (/^claude-(?:opus|sonnet)-4-6(?:-v1)?$/.test(id)) return ["low", "medium", "high", "max"];
|
||||
return ["low", "medium", "high"];
|
||||
}
|
||||
export const label = "Claude Code";
|
||||
|
||||
export const SANDBOX_INSTALL_COMMAND = "npm install -g @anthropic-ai/claude-code";
|
||||
@@ -32,6 +42,7 @@ export const models = [
|
||||
{ id: "claude-fable-5-1", label: "Claude Fable 5.1" },
|
||||
{ id: "claude-fable-5", label: "Claude Fable 5" },
|
||||
{ id: "claude-mythos-5", label: "Claude Mythos 5" },
|
||||
{ id: "claude-opus-5-5", label: "Claude Opus 5.5" },
|
||||
{ id: "claude-opus-5", label: "Claude Opus 5" },
|
||||
{ id: "claude-opus-4-7", label: "Claude Opus 4.7" },
|
||||
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
|
||||
@@ -49,7 +60,7 @@ Core fields:
|
||||
- cwd (string, optional): default absolute working directory fallback for the agent process (created if missing when possible)
|
||||
- instructionsFilePath (string, optional): absolute path to a markdown instructions file injected at runtime
|
||||
- model (string, optional): Claude model id. Missing or blank defaults to ${DEFAULT_CLAUDE_LOCAL_MODEL} in both CLI and ACP, including existing agents. Explicit model IDs and ANTHROPIC_MODEL overrides are preserved. Bedrock/Vertex without an explicit model retain their provider default.
|
||||
- effort (string, optional): reasoning effort passed via --effort (low|medium|high)
|
||||
- effort (string, optional): model-specific reasoning effort passed via --effort (low|medium|high; current Opus, Sonnet 5, and Fable models also support xhigh|max)
|
||||
- chrome (boolean, optional): pass --chrome when running Claude
|
||||
- promptTemplate (string, optional): run prompt template
|
||||
- maxTurnsPerRun (number, optional): max turns for one run
|
||||
@@ -77,6 +88,7 @@ Operational fields:
|
||||
- graceSec (number, optional): SIGTERM grace period in seconds
|
||||
|
||||
Notes:
|
||||
- Claude Opus 5.5 uses model ID \`claude-opus-5-5\` and requires Claude Code v2.1.280 or later.
|
||||
- filesystemScope and networkScope are spawn-level confinement and are orthogonal to Claude permission flags. Both require Bubblewrap on the host and explicit engine="cli"; default or explicit ACP is rejected because ACP confinement is not yet supported. networkScope="allowlist" injects HTTP_PROXY/HTTPS_PROXY for the CLI while its private network namespace blocks direct sockets, so every required provider/API hostname must be listed explicitly.
|
||||
- The Claude ACP lane requires Node >=24.11.0 and @agentclientprotocol/claude-agent-acp to be installed with this adapter package. Missing prerequisites fail both default and explicit ACP runs with an actionable setup error; the adapter never switches engines automatically.
|
||||
- For ACP runs, model selection is passed through ANTHROPIC_MODEL at ACP server startup; Paperclip-managed Claude permissions and ephemeral skill materialization are handled by the shared ACP engine.
|
||||
|
||||
@@ -6,11 +6,6 @@ const effortFlagSupportCache = new Map<string, Promise<boolean | null>>();
|
||||
|
||||
export const CLAUDE_FABLE_5_1_MIN_CLI_VERSION = "2.1.251";
|
||||
|
||||
const CLAUDE_FABLE_5_1_MODEL_IDS = new Set([
|
||||
"claude-fable-5-1",
|
||||
"us.anthropic.claude-fable-5-1",
|
||||
]);
|
||||
|
||||
export function claudeCommandLooksLike(command: string, expected = "claude"): boolean {
|
||||
const base = path.basename(command).toLowerCase();
|
||||
return base === expected || base === `${expected}.cmd` || base === `${expected}.exe`;
|
||||
@@ -41,7 +36,9 @@ function cacheKeyForTarget(command: string, target: AdapterExecutionTarget | nul
|
||||
}
|
||||
|
||||
export function minimumClaudeCliVersionForModel(model: string): string | null {
|
||||
return CLAUDE_FABLE_5_1_MODEL_IDS.has(model.trim())
|
||||
const modelId = model.trim().replace(/\[1m\]$/, "").replace(/^(?:(?:us|eu|apac|global)\.)?anthropic\./, "");
|
||||
if (modelId === "claude-opus-5-5") return "2.1.280";
|
||||
return modelId === "claude-fable-5-1"
|
||||
? CLAUDE_FABLE_5_1_MIN_CLI_VERSION
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const {
|
||||
signal: null,
|
||||
timedOut: false,
|
||||
stdout: args.includes("--version")
|
||||
? "2.1.251 (Claude Code)\n"
|
||||
? "2.1.280 (Claude Code)\n"
|
||||
: [
|
||||
JSON.stringify({ type: "system", subtype: "init", session_id: "claude-session-1", model: "claude-sonnet" }),
|
||||
JSON.stringify({ type: "assistant", session_id: "claude-session-1", message: { content: [{ type: "text", text: "hello" }] } }),
|
||||
@@ -466,14 +466,14 @@ describe("claude remote execution", () => {
|
||||
return { args: call?.[2] ?? [], result };
|
||||
}
|
||||
|
||||
it("passes the exact configured Fable 5.1 ID as --model on the CLI lane", async () => {
|
||||
it.each(["claude-fable-5-1", "claude-opus-5-5"])("passes %s as --model on the CLI lane", async (model) => {
|
||||
const { args } = await executeWithModel("paperclip-claude-model-direct-", {
|
||||
model: "claude-fable-5-1",
|
||||
model,
|
||||
});
|
||||
|
||||
const modelFlag = args.indexOf("--model");
|
||||
expect(modelFlag).toBeGreaterThanOrEqual(0);
|
||||
expect(args[modelFlag + 1]).toBe("claude-fable-5-1");
|
||||
expect(args[modelFlag + 1]).toBe(model);
|
||||
});
|
||||
|
||||
it("passes the Bedrock-native Fable 5.1 ID as --model under Bedrock auth", async () => {
|
||||
@@ -496,27 +496,30 @@ describe("claude remote execution", () => {
|
||||
expect(args).not.toContain("--model");
|
||||
});
|
||||
|
||||
it("rejects Fable 5.1 before launch when the CLI is older than 2.1.251", async () => {
|
||||
it.each([
|
||||
["claude-fable-5-1", "2.1.251", "2.1.247"],
|
||||
["claude-opus-5-5", "2.1.280", "2.1.279"],
|
||||
])("rejects %s before launch below CLI %s", async (model, minimumVersion, detectedVersion) => {
|
||||
runChildProcess.mockResolvedValueOnce({
|
||||
exitCode: 0,
|
||||
signal: null,
|
||||
timedOut: false,
|
||||
stdout: "2.1.247 (Claude Code)\n",
|
||||
stdout: `${detectedVersion} (Claude Code)\n`,
|
||||
stderr: "",
|
||||
pid: 123,
|
||||
startedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const { args, result } = await executeWithModel("paperclip-claude-model-old-cli-", {
|
||||
model: "claude-fable-5-1",
|
||||
model,
|
||||
});
|
||||
|
||||
expect(args).toEqual([]);
|
||||
expect(result.errorCode).toBe("claude_cli_version_incompatible");
|
||||
expect(result.errorMessage).toContain("requires Claude Code 2.1.251 or newer");
|
||||
expect(result.errorMessage).toContain(`${model} requires Claude Code ${minimumVersion} or newer`);
|
||||
expect(result.resultJson).toMatchObject({
|
||||
requiredClaudeCodeVersion: "2.1.251",
|
||||
detectedClaudeCodeVersion: "2.1.247",
|
||||
requiredClaudeCodeVersion: minimumVersion,
|
||||
detectedClaudeCodeVersion: detectedVersion,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1286,7 +1286,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
||||
? `detected ${detectedCliVersion}`
|
||||
: "could not determine the installed version";
|
||||
const errorMessage =
|
||||
`Claude Fable 5.1 requires Claude Code ${minimumCliVersion} or newer on the CLI lane; ${detected}. ` +
|
||||
`${model} requires Claude Code ${minimumCliVersion} or newer on the CLI lane; ${detected}. ` +
|
||||
"Upgrade Claude Code or restore the default ACP lane before retrying.";
|
||||
await onLog("stderr", `[paperclip] ${errorMessage}\n`);
|
||||
return {
|
||||
|
||||
@@ -9,10 +9,15 @@ const ANTHROPIC_API_VERSION = "2023-06-01";
|
||||
|
||||
/** AWS Bedrock model IDs — region-qualified identifiers required by the Bedrock API. */
|
||||
const BEDROCK_MODELS: AdapterModel[] = [
|
||||
{ id: "us.anthropic.claude-opus-4-8-v1", label: "Bedrock Opus 4.8" },
|
||||
{ id: "us.anthropic.claude-opus-4-8", label: "Bedrock Opus 4.8" },
|
||||
{ id: "us.anthropic.claude-opus-5-5", label: "Bedrock Opus 5.5" },
|
||||
{ id: "us.anthropic.claude-opus-5", label: "Bedrock Opus 5" },
|
||||
{ id: "us.anthropic.claude-sonnet-5", label: "Bedrock Sonnet 5" },
|
||||
// Fable 5.1's documented geo inference ID carries no -v1 suffix, unlike earlier entries.
|
||||
{ id: "us.anthropic.claude-fable-5-1", label: "Bedrock Fable 5.1" },
|
||||
{ id: "us.anthropic.claude-fable-5-v1", label: "Bedrock Fable 5" },
|
||||
{ id: "us.anthropic.claude-fable-5", label: "Bedrock Fable 5" },
|
||||
{ id: "us.anthropic.claude-opus-4-7", label: "Bedrock Opus 4.7" },
|
||||
{ id: "us.anthropic.claude-sonnet-4-6", label: "Bedrock Sonnet 4.6" },
|
||||
{ id: "us.anthropic.claude-opus-4-6-v1", label: "Bedrock Opus 4.6" },
|
||||
{ id: "us.anthropic.claude-sonnet-4-5-20250929-v2:0", label: "Bedrock Sonnet 4.5" },
|
||||
{ id: "us.anthropic.claude-haiku-4-5-20251001-v1:0", label: "Bedrock Haiku 4.5" },
|
||||
|
||||
@@ -639,14 +639,17 @@ describe("claude CLI local hello probe hardening", () => {
|
||||
expect(JSON.stringify(spawnedEnv)).not.toContain("caller-proxy");
|
||||
});
|
||||
|
||||
it("warns without executing when runtime PATH selects a different local Claude executable", async () => {
|
||||
it.each([
|
||||
["claude-fable-5-1", "2.1.251"],
|
||||
["claude-opus-5-5", "2.1.280"],
|
||||
])("warns without executing %s when runtime PATH selects a different executable", async (model, minimumVersion) => {
|
||||
const runtimeDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-cli-runtime-path-"));
|
||||
const runtimeClaudePath = path.join(runtimeDir, "claude");
|
||||
await writeFile(runtimeClaudePath, "#!/bin/sh\nexit 0\n");
|
||||
await chmod(runtimeClaudePath, 0o755);
|
||||
|
||||
try {
|
||||
probeResult.value = { exitCode: 0, stdout: "2.1.251 (Claude Code)\n", stderr: "" };
|
||||
probeResult.value = { exitCode: 0, stdout: `${minimumVersion} (Claude Code)\n`, stderr: "" };
|
||||
|
||||
const result = await testEnvironment({
|
||||
companyId: "company-1",
|
||||
@@ -654,7 +657,7 @@ describe("claude CLI local hello probe hardening", () => {
|
||||
config: {
|
||||
engine: "cli",
|
||||
command: "claude",
|
||||
model: "claude-fable-5-1",
|
||||
model,
|
||||
env: { PATH: runtimeDir },
|
||||
},
|
||||
executionTarget: null,
|
||||
@@ -664,6 +667,7 @@ describe("claude CLI local hello probe hardening", () => {
|
||||
expect(result.status).toBe("warn");
|
||||
expect(result.checks).toContainEqual(expect.objectContaining({
|
||||
code: "claude_cli_version_probe_mismatch",
|
||||
hint: `Ensure the runtime-selected Claude Code is ${minimumVersion} or newer. Execution will verify that exact executable before launch.`,
|
||||
level: "warn",
|
||||
}));
|
||||
expect(runAdapterExecutionTargetProcess).not.toHaveBeenCalled();
|
||||
|
||||
@@ -141,10 +141,13 @@ describe("claude sandbox auth-missing check", () => {
|
||||
});
|
||||
|
||||
describe("claude CLI model compatibility check", () => {
|
||||
it("fails before the hello probe when Fable 5.1 is configured with an older CLI", async () => {
|
||||
it.each([
|
||||
["claude-fable-5-1", "2.1.251", "2.1.247"],
|
||||
["claude-opus-5-5", "2.1.280", "2.1.279"],
|
||||
])("fails before the hello probe for %s below CLI %s", async (model, minimumVersion, detectedVersion) => {
|
||||
probeResult.value = {
|
||||
exitCode: 0,
|
||||
stdout: "2.1.247 (Claude Code)\n",
|
||||
stdout: `${detectedVersion} (Claude Code)\n`,
|
||||
stderr: "",
|
||||
};
|
||||
|
||||
@@ -154,7 +157,7 @@ describe("claude CLI model compatibility check", () => {
|
||||
config: {
|
||||
engine: "cli",
|
||||
command: "claude",
|
||||
model: "claude-fable-5-1",
|
||||
model,
|
||||
},
|
||||
executionTarget: sandboxTarget,
|
||||
environmentName: "Daytona",
|
||||
@@ -164,7 +167,8 @@ describe("claude CLI model compatibility check", () => {
|
||||
expect(result.checks).toContainEqual(expect.objectContaining({
|
||||
code: "claude_cli_version_incompatible",
|
||||
level: "error",
|
||||
detail: "Detected Claude Code 2.1.247.",
|
||||
message: `${model} requires Claude Code ${minimumVersion} or newer on the CLI lane.`,
|
||||
detail: `Detected Claude Code ${detectedVersion}.`,
|
||||
}));
|
||||
expect(runAdapterExecutionTargetProcess).toHaveBeenCalledTimes(1);
|
||||
const versionCall = runAdapterExecutionTargetProcess.mock.calls[0] as unknown as [
|
||||
|
||||
@@ -266,9 +266,9 @@ export async function testEnvironment(
|
||||
code: "claude_cli_version_probe_mismatch",
|
||||
level: "warn",
|
||||
message:
|
||||
"Skipped Fable 5.1 readiness probing because the runtime PATH selects a different Claude executable than the trusted local Test probe.",
|
||||
`Skipped ${configuredModel} readiness probing because the runtime PATH selects a different Claude executable than the trusted local Test probe.`,
|
||||
hint:
|
||||
"Ensure the runtime-selected Claude Code is 2.1.251 or newer. Execution will verify that exact executable before launch.",
|
||||
`Ensure the runtime-selected Claude Code is ${minimumCliVersion} or newer. Execution will verify that exact executable before launch.`,
|
||||
});
|
||||
} else if (canRunProbe && minimumCliVersion && versionProbeCommand) {
|
||||
const versionProbeEnv = localProbe?.env ?? env;
|
||||
@@ -289,7 +289,7 @@ export async function testEnvironment(
|
||||
checks.push({
|
||||
code: "claude_cli_version_incompatible",
|
||||
level: "error",
|
||||
message: `Claude Fable 5.1 requires Claude Code ${minimumCliVersion} or newer on the CLI lane.`,
|
||||
message: `${configuredModel} requires Claude Code ${minimumCliVersion} or newer on the CLI lane.`,
|
||||
detail: detectedCliVersion
|
||||
? `Detected Claude Code ${detectedCliVersion}.`
|
||||
: "Could not determine the installed Claude Code version.",
|
||||
|
||||
@@ -14,21 +14,24 @@ describe("codex local adapter metadata", () => {
|
||||
// Default to the concrete gpt-5.6-sol slug — Codex ships no metadata for the bare gpt-5.6
|
||||
// alias, so it must not be advertised or used as the default (it triggers a fallback warning).
|
||||
expect(DEFAULT_CODEX_LOCAL_MODEL).toBe("gpt-5.6-sol");
|
||||
expect(modelIds.slice(0, 4)).toEqual([
|
||||
expect(modelIds.slice(0, 6)).toEqual([
|
||||
"gpt-5.6-sol",
|
||||
"gpt-6-astra",
|
||||
"gpt-6-sol",
|
||||
"gpt-6-luna",
|
||||
"gpt-5.6-terra",
|
||||
"gpt-5.6-luna",
|
||||
]);
|
||||
expect(modelIds).not.toContain("gpt-5.6");
|
||||
expect(isCodexLocalFastModeSupported(DEFAULT_CODEX_LOCAL_MODEL)).toBe(true);
|
||||
expect(isCodexLocalFastModeSupported("gpt-6-astra")).toBe(true);
|
||||
expect(isCodexLocalFastModeSupported("gpt-6-sol")).toBe(true);
|
||||
expect(modelIds).not.toContain("gpt-5.3-codex");
|
||||
expect(modelIds).not.toContain("gpt-5.3-codex-spark");
|
||||
});
|
||||
|
||||
it("uses the reasoning efforts advertised for GPT-6 Astra", () => {
|
||||
expect(codexLocalReasoningEffortsForModel("gpt-6-astra")).toEqual([
|
||||
it.each(["gpt-6-astra", "gpt-6-sol", " gpt-6-sol ", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6"])("uses the reasoning efforts advertised for %s", (model) => {
|
||||
expect(codexLocalReasoningEffortsForModel(model)).toEqual([
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
@@ -36,7 +39,10 @@ describe("codex local adapter metadata", () => {
|
||||
"max",
|
||||
"ultra",
|
||||
]);
|
||||
expect(codexLocalReasoningEffortsForModel("gpt-5.6-sol")).toEqual([
|
||||
});
|
||||
|
||||
it.each(["gpt-5.5", "custom-model"])("preserves legacy efforts for %s", (model) => {
|
||||
expect(codexLocalReasoningEffortsForModel(model)).toEqual([
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
@@ -45,6 +51,11 @@ describe("codex local adapter metadata", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it.each(["gpt-6-luna", "gpt-5.6-luna"])("caps %s at max and supports Fast mode", (model) => {
|
||||
expect(codexLocalReasoningEffortsForModel(model)).toEqual(["low", "medium", "high", "xhigh", "max"]);
|
||||
expect(isCodexLocalFastModeSupported(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("normalizes the legacy bare gpt-5.6 alias to the concrete gpt-5.6-sol slug", () => {
|
||||
expect(normalizeCodexModel("gpt-5.6")).toBe("gpt-5.6-sol");
|
||||
expect(normalizeCodexModel(" gpt-5.6 ")).toBe("gpt-5.6-sol");
|
||||
|
||||
@@ -14,6 +14,8 @@ export const DEFAULT_CODEX_LOCAL_MODEL = PAPERCLIP_RUNNER_DEFAULT_MODELS.codex;
|
||||
export const DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX = true;
|
||||
export const CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS = [
|
||||
"gpt-6-astra",
|
||||
"gpt-6-sol",
|
||||
"gpt-6-luna",
|
||||
"gpt-5.6-sol",
|
||||
"gpt-5.6-terra",
|
||||
"gpt-5.6-luna",
|
||||
@@ -41,18 +43,22 @@ const CODEX_LOCAL_DEFAULT_REASONING_EFFORTS = [
|
||||
"xhigh",
|
||||
] as const;
|
||||
|
||||
const CODEX_LOCAL_ASTRA_REASONING_EFFORTS = [
|
||||
const CODEX_LOCAL_MAX_REASONING_EFFORTS = [
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh",
|
||||
"max",
|
||||
] as const;
|
||||
|
||||
const CODEX_LOCAL_ULTRA_REASONING_EFFORTS = [
|
||||
...CODEX_LOCAL_MAX_REASONING_EFFORTS,
|
||||
"ultra",
|
||||
] as const;
|
||||
|
||||
export type CodexLocalReasoningEffort =
|
||||
| (typeof CODEX_LOCAL_DEFAULT_REASONING_EFFORTS)[number]
|
||||
| (typeof CODEX_LOCAL_ASTRA_REASONING_EFFORTS)[number];
|
||||
| (typeof CODEX_LOCAL_ULTRA_REASONING_EFFORTS)[number];
|
||||
|
||||
export function normalizeCodexModel(model: string | null | undefined): string {
|
||||
const normalizedModel = normalizeModelId(model);
|
||||
@@ -62,9 +68,19 @@ export function normalizeCodexModel(model: string | null | undefined): string {
|
||||
export function codexLocalReasoningEffortsForModel(
|
||||
model: string | null | undefined,
|
||||
): readonly CodexLocalReasoningEffort[] {
|
||||
return normalizeCodexModel(model) === "gpt-6-astra"
|
||||
? CODEX_LOCAL_ASTRA_REASONING_EFFORTS
|
||||
: CODEX_LOCAL_DEFAULT_REASONING_EFFORTS;
|
||||
const normalizedModel = normalizeCodexModel(model);
|
||||
switch (normalizedModel) {
|
||||
case "gpt-6-astra":
|
||||
case "gpt-6-sol":
|
||||
case "gpt-5.6-sol":
|
||||
case "gpt-5.6-terra":
|
||||
return CODEX_LOCAL_ULTRA_REASONING_EFFORTS;
|
||||
case "gpt-6-luna":
|
||||
case "gpt-5.6-luna":
|
||||
return CODEX_LOCAL_MAX_REASONING_EFFORTS;
|
||||
default:
|
||||
return CODEX_LOCAL_DEFAULT_REASONING_EFFORTS;
|
||||
}
|
||||
}
|
||||
|
||||
export function isCodexLocalKnownModel(model: string | null | undefined): boolean {
|
||||
@@ -94,6 +110,8 @@ export const models = [
|
||||
// DEFAULT_CODEX_LOCAL_MODEL is gpt-5.6-sol, so it doubles as the first (default) 5.6 entry.
|
||||
{ id: DEFAULT_CODEX_LOCAL_MODEL, label: DEFAULT_CODEX_LOCAL_MODEL },
|
||||
{ id: "gpt-6-astra", label: "gpt-6-astra" },
|
||||
{ id: "gpt-6-sol", label: "gpt-6-sol" },
|
||||
{ id: "gpt-6-luna", label: "gpt-6-luna" },
|
||||
{ id: "gpt-5.6-terra", label: "gpt-5.6-terra" },
|
||||
{ id: "gpt-5.6-luna", label: "gpt-5.6-luna" },
|
||||
{ id: "gpt-5.4", label: "gpt-5.4" },
|
||||
@@ -116,10 +134,10 @@ Core fields:
|
||||
- cwd (string, optional): default absolute working directory fallback for the agent process (created if missing when possible)
|
||||
- instructionsFilePath (string, optional): absolute path to a markdown instructions file prepended to stdin prompt at runtime
|
||||
- model (string, optional): Codex model id
|
||||
- modelReasoningEffort (string, optional): reasoning effort override passed via -c model_reasoning_effort=...; GPT-6 Astra supports low|medium|high|xhigh|max|ultra
|
||||
- modelReasoningEffort (string, optional): reasoning effort override passed via -c model_reasoning_effort=...; GPT-6 Astra/Sol and GPT-5.6 Sol/Terra support low|medium|high|xhigh|max|ultra; GPT-6 Luna and GPT-5.6 Luna support low|medium|high|xhigh|max
|
||||
- promptTemplate (string, optional): run prompt template
|
||||
- search (boolean, optional): run codex with --search
|
||||
- fastMode (boolean, optional): enable Codex Fast mode; supported on GPT-6 Astra, GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and passed through for manual model IDs
|
||||
- fastMode (boolean, optional): enable Codex Fast mode; supported on GPT-6 (astra/sol/luna), GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and passed through for manual model IDs
|
||||
- dangerouslyBypassApprovalsAndSandbox (boolean, optional): run with bypass flag
|
||||
- command (string, optional): defaults to "codex"
|
||||
- extraArgs (string[], optional): additional CLI args
|
||||
@@ -150,7 +168,7 @@ Notes:
|
||||
- Paperclip injects desired local skills into the effective CODEX_HOME/skills/ directory at execution time so Codex can discover "$paperclip" and related skills without polluting the project working directory. For new and updated agents, Paperclip assigns an isolated managed home at ~/.paperclip/instances/<id>/companies/<companyId>/agents/<agentId>/codex-home/skills/; when CODEX_HOME is explicitly overridden in adapter config, that override is used instead.
|
||||
- New and updated codex_local agents persist an empty OPENAI_API_KEY override by default so a host-level OPENAI_API_KEY cannot leak into Codex runs through process inheritance. Explicit CODEX_HOME overrides must not point at the shared company codex-home, $CODEX_HOME, or ~/.codex.
|
||||
- Some model/tool combinations reject certain effort levels (for example minimal with web search enabled).
|
||||
- Fast mode is supported on GPT-6 Astra, GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and manual model IDs. When enabled for those models, Paperclip applies \`service_tier="fast"\` and \`features.fast_mode=true\`.
|
||||
- Fast mode is supported on GPT-6 (astra/sol/luna), GPT-5.6 (sol/terra/luna), GPT-5.5, GPT-5.4 and manual model IDs. When enabled for those models, Paperclip applies \`service_tier="fast"\` and \`features.fast_mode=true\`.
|
||||
- When Paperclip realizes a workspace/runtime for a run, it injects PAPERCLIP_WORKSPACE_* and PAPERCLIP_RUNTIME_* env vars for agent-side tooling.
|
||||
- The ACP engine keeps its workspace sandbox and enables network access on each turn. Explicit sandbox_workspace_write.network_access overrides in extraArgs (or env.PAPERCLIP_CODEX_ACP_NETWORK_ACCESS="false") disable it; execution-target network denial wins. The bundled ACP patch is needed because upstream mode presets override Codex config.toml on every turn.
|
||||
- The CLI engine defaults to a writable workspace sandbox with network access for unattended work and Paperclip API calls. It does not enable the dangerous bypass flag. Explicit sandbox modes/profiles and network overrides in extraArgs retain their meaning. An execution-target network denial remains enforced.
|
||||
|
||||
@@ -508,15 +508,15 @@ describe("codex_local ACP lane", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards GPT-6 Astra controls to the ACPX Codex target", () => {
|
||||
it.each([["gpt-6-astra", "ultra"], ["gpt-6-sol", "ultra"], ["gpt-6-luna", "max"], ["gpt-5.6-sol", "ultra"], ["gpt-5.6-terra", "ultra"], ["gpt-5.6-luna", "max"]])("forwards %s controls to the ACPX Codex target", (model, effort) => {
|
||||
expect(buildCodexAcpConfig({
|
||||
engine: "acp",
|
||||
model: "gpt-6-astra",
|
||||
modelReasoningEffort: "ultra",
|
||||
model,
|
||||
modelReasoningEffort: effort,
|
||||
fastMode: true,
|
||||
})).toMatchObject({
|
||||
model: "gpt-6-astra",
|
||||
modelReasoningEffort: "ultra",
|
||||
model,
|
||||
modelReasoningEffort: effort,
|
||||
fastMode: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,14 +9,14 @@ describe("buildCodexExecArgs", () => {
|
||||
if (resumeSessionId) expect(args.slice(-3)).toEqual(["resume", resumeSessionId, "-"]);
|
||||
});
|
||||
|
||||
it("forwards GPT-6 Astra, its ultra reasoning effort, and fast mode", () => {
|
||||
it.each([["gpt-6-astra", "ultra"], ["gpt-6-sol", "ultra"], ["gpt-6-luna", "max"], ["gpt-5.6-sol", "ultra"], ["gpt-5.6-terra", "ultra"], ["gpt-5.6-luna", "max"]])("forwards %s, its supported reasoning effort, and fast mode", (model, effort) => {
|
||||
const result = buildCodexExecArgs({
|
||||
model: "gpt-6-astra",
|
||||
modelReasoningEffort: "ultra",
|
||||
model,
|
||||
modelReasoningEffort: effort,
|
||||
fastMode: true,
|
||||
});
|
||||
|
||||
expect(result.model).toBe("gpt-6-astra");
|
||||
expect(result.model).toBe(model);
|
||||
expect(result.fastModeApplied).toBe(true);
|
||||
expect(result.fastModeIgnoredReason).toBeNull();
|
||||
expect(result.args).toEqual([
|
||||
@@ -24,9 +24,9 @@ describe("buildCodexExecArgs", () => {
|
||||
"--json",
|
||||
"--dangerously-bypass-approvals-and-sandbox",
|
||||
"--model",
|
||||
"gpt-6-astra",
|
||||
model,
|
||||
"-c",
|
||||
'model_reasoning_effort="ultra"',
|
||||
`model_reasoning_effort="${effort}"`,
|
||||
"-c",
|
||||
'service_tier="fast"',
|
||||
"-c",
|
||||
@@ -148,7 +148,7 @@ describe("buildCodexExecArgs", () => {
|
||||
expect(result.fastModeRequested).toBe(true);
|
||||
expect(result.fastModeApplied).toBe(false);
|
||||
expect(result.fastModeIgnoredReason).toContain(
|
||||
"currently only supported on gpt-6-astra, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4 or manually configured model IDs",
|
||||
"currently only supported on gpt-6-astra, gpt-6-sol, gpt-6-luna, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4 or manually configured model IDs",
|
||||
);
|
||||
expect(result.args).toEqual([
|
||||
"exec",
|
||||
|
||||
@@ -63,18 +63,18 @@ describe("buildCodexLocalConfig", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("persists the exact GPT-6 Astra model and supported controls", () => {
|
||||
it.each([["gpt-6-astra", "ultra"], ["gpt-6-sol", "ultra"], ["gpt-6-luna", "max"], ["gpt-5.6-sol", "ultra"], ["gpt-5.6-terra", "ultra"], ["gpt-5.6-luna", "max"]])("persists the exact %s model and supported controls", (model, effort) => {
|
||||
const config = buildCodexLocalConfig(
|
||||
makeValues({
|
||||
model: "gpt-6-astra",
|
||||
thinkingEffort: "ultra",
|
||||
model,
|
||||
thinkingEffort: effort,
|
||||
fastMode: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(config).toMatchObject({
|
||||
model: "gpt-6-astra",
|
||||
modelReasoningEffort: "ultra",
|
||||
model,
|
||||
modelReasoningEffort: effort,
|
||||
fastMode: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,18 @@ export const DEFAULT_CURSOR_LOCAL_MODEL = "auto";
|
||||
|
||||
const CURSOR_FALLBACK_MODEL_IDS = [
|
||||
"auto",
|
||||
"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",
|
||||
"gemini-3.8-flash",
|
||||
"muse-spark-1.3",
|
||||
"grok-4.7",
|
||||
"grok-4.6",
|
||||
"grok-4.5",
|
||||
"composer-1.5",
|
||||
"composer-1",
|
||||
"gpt-5.3-codex-low",
|
||||
|
||||
@@ -13,11 +13,16 @@ export const models = [
|
||||
{ id: DEFAULT_GEMINI_LOCAL_MODEL, label: "Auto" },
|
||||
{ id: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro Preview" },
|
||||
{ id: "gemini-3.1-pro-preview-customtools", label: "Gemini 3.1 Pro Preview (Custom Tools)" },
|
||||
{ id: "gemini-3.8-flash", label: "Gemini 3.8 Flash" },
|
||||
{ id: "gemini-3.7-flash", label: "Gemini 3.7 Flash" },
|
||||
{ id: "gemini-3.6-flash", label: "Gemini 3.6 Flash" },
|
||||
{ id: "gemini-3.5-flash", label: "Gemini 3.5 Flash" },
|
||||
{ id: "gemini-3.5-flash-lite", label: "Gemini 3.5 Flash Lite" },
|
||||
{ id: "gemini-3.1-flash-lite", label: "Gemini 3.1 Flash Lite" },
|
||||
{ id: "gemini-3-flash-preview", label: "Gemini 3 Flash Preview" },
|
||||
{ id: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
|
||||
{ id: "gemini-2.5-flash", label: "Gemini 2.5 Flash" },
|
||||
{ id: "gemini-2.5-flash-lite", label: "Gemini 2.5 Flash Lite" },
|
||||
{ id: "gemini-2.0-flash", label: "Gemini 2.0 Flash" },
|
||||
{ id: "gemini-2.0-flash-lite", label: "Gemini 2.0 Flash Lite" },
|
||||
];
|
||||
|
||||
export const agentConfigurationDoc = `# gemini_local agent configuration
|
||||
|
||||
@@ -5,8 +5,17 @@ export const DEFAULT_GROK_LOCAL_MODEL = "grok-build";
|
||||
|
||||
export const models = [
|
||||
{ id: DEFAULT_GROK_LOCAL_MODEL, label: DEFAULT_GROK_LOCAL_MODEL },
|
||||
{ id: "grok-4.7", label: "Grok 4.7" },
|
||||
{ id: "grok-4.6", label: "Grok 4.6" },
|
||||
{ id: "grok-4.5", label: "Grok 4.5" },
|
||||
];
|
||||
|
||||
export function grokLocalReasoningEffortsForModel(model: string): readonly string[] {
|
||||
return model.trim() === "grok-4.7" || model.trim() === "grok-4.6"
|
||||
? ["low", "medium", "high", "xhigh"]
|
||||
: ["low", "medium", "high"];
|
||||
}
|
||||
|
||||
export const agentConfigurationDoc = `# grok_local agent configuration
|
||||
|
||||
Adapter: grok_local
|
||||
@@ -27,7 +36,7 @@ Core fields:
|
||||
- promptTemplate (string, optional): run prompt template
|
||||
- model (string, optional): Grok model id. Defaults to grok-build.
|
||||
- permissionMode (string, optional): Grok permission mode passed via \`--permission-mode\`. Unset by default: Grok >= 1.0 enforces \`dontAsk\` as deny-by-default and it overrides \`--always-approve\`, so unattended runs rely on \`--always-approve\` alone unless you explicitly need a mode
|
||||
- reasoningEffort (string, optional): Grok reasoning effort passed via \`--reasoning-effort\`
|
||||
- reasoningEffort (string, optional): Grok reasoning effort (low|medium|high; grok-4.7 and grok-4.6 also accept xhigh) passed via \`--reasoning-effort\`
|
||||
- maxTurns (number, optional): maximum agent turns for the run
|
||||
- command (string, optional): defaults to "grok"
|
||||
- extraArgs (string[], optional): additional CLI args
|
||||
|
||||
@@ -160,6 +160,19 @@ async function makeCtx(runId: string, cwd: string): Promise<AdapterExecutionCont
|
||||
}
|
||||
|
||||
describe("grok_local execute", () => {
|
||||
it.each(["grok-4.7", "grok-4.6"])("forwards the explicit %s model and xhigh effort", async (model) => {
|
||||
const root = await makeTempRoot();
|
||||
const ctx = await makeCtx("model-selection", root);
|
||||
ctx.config = { cwd: root, model, reasoningEffort: "xhigh" };
|
||||
runProcessMock.mockResolvedValue(makeSuccessfulRunResult());
|
||||
|
||||
await execute(ctx);
|
||||
|
||||
const args = runProcessMock.mock.calls[0][3] as string[];
|
||||
expect(args[args.indexOf("--model") + 1]).toBe(model);
|
||||
expect(args[args.indexOf("--reasoning-effort") + 1]).toBe("xhigh");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.state.isRemote = false;
|
||||
mocks.state.prepareRuntimeResult = null;
|
||||
|
||||
@@ -8,9 +8,10 @@ export const SANDBOX_INSTALL_COMMAND = buildSandboxNpmInstallCommand("@moonshot-
|
||||
export const DEFAULT_KIMI_LOCAL_MODEL = "kimi-code/kimi-for-coding";
|
||||
|
||||
export const models = [
|
||||
{ id: DEFAULT_KIMI_LOCAL_MODEL, label: "K2.7 Coding" },
|
||||
{ id: DEFAULT_KIMI_LOCAL_MODEL, label: "K2.8 Preview" },
|
||||
{ id: "kimi-code/kimi-for-coding-highspeed", label: "K2.7 Coding Highspeed" },
|
||||
{ id: "kimi-code/k3", label: "K3" },
|
||||
{ id: "kimi-code/k3-256k", label: "K3 (256K)" },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,11 @@ export type KimiEffort = (typeof KIMI_SUPPORTED_EFFORTS)[number];
|
||||
* Models that advertise `support_efforts` in Kimi's model catalog. Keep in
|
||||
* sync with `models` above; only these accept KIMI_MODEL_THINKING_EFFORT.
|
||||
*/
|
||||
export const EFFORT_CAPABLE_MODELS = new Set<string>(["kimi-code/k3"]);
|
||||
export const EFFORT_CAPABLE_MODELS = new Set<string>([
|
||||
DEFAULT_KIMI_LOCAL_MODEL,
|
||||
"kimi-code/k3",
|
||||
"kimi-code/k3-256k",
|
||||
]);
|
||||
|
||||
export function modelSupportsEffort(model: string): boolean {
|
||||
return EFFORT_CAPABLE_MODELS.has(model.trim());
|
||||
@@ -65,7 +70,7 @@ Core fields:
|
||||
- instructionsFilePath (string, optional): absolute path to a markdown instructions file prepended to the run prompt. Sibling files in the same directory (HEARTBEAT.md, SOUL.md, TOOLS.md) are made readable via --add-dir for local runs.
|
||||
- promptTemplate (string, optional): run prompt template
|
||||
- model (string, optional): Kimi model alias (provider/model). Defaults to kimi-code/kimi-for-coding.
|
||||
- effort (string, optional): thinking effort (low | medium | high | max). CLI lane only (engine=cli): forwarded as KIMI_MODEL_THINKING_EFFORT for effort-capable models (currently kimi-code/k3); "medium" maps to "high" since Kimi has no medium tier. Ignored for models without support_efforts, and NOT forwarded on the default ACP engine lane (Kimi ACP exposes a separate "thinking" option that is not wired yet) — pin engine=cli when effort control matters.
|
||||
- effort (string, optional): thinking effort (low | medium | high | max). CLI lane only (engine=cli): forwarded as KIMI_MODEL_THINKING_EFFORT for effort-capable models (K2.8 Preview, K3, and K3 256K); "medium" maps to "high" since Kimi has no medium tier. Ignored for models without support_efforts, and NOT forwarded on the default ACP engine lane (Kimi ACP exposes a separate "thinking" option that is not wired yet) — pin engine=cli when effort control matters.
|
||||
- command (string, optional): defaults to "kimi"
|
||||
- extraArgs (string[], optional): additional CLI args
|
||||
- env (object, optional): KEY=VALUE environment variables
|
||||
|
||||
@@ -333,7 +333,7 @@ describe("kimi_local execute", () => {
|
||||
expect(seenEnv.TERM).toBe("xterm-256color");
|
||||
});
|
||||
|
||||
it("forwards configured effort as KIMI_MODEL_THINKING_EFFORT for effort-capable models", async () => {
|
||||
it.each(["kimi-code/k3", "kimi-code/k3-256k", "kimi-code/kimi-for-coding"])("forwards configured effort for %s", async (model) => {
|
||||
const root = await makeTempRoot();
|
||||
let seenEnv: Record<string, string> = {};
|
||||
runProcessMock.mockImplementation(async (_runId, _target, _command, _args, options) => {
|
||||
@@ -341,7 +341,7 @@ describe("kimi_local execute", () => {
|
||||
return { exitCode: 0, signal: null, timedOut: false, stdout: KIMI_STDOUT, stderr: "" };
|
||||
});
|
||||
|
||||
await execute(makeContext(root, { config: { cwd: root, model: "kimi-code/k3", effort: "high" } }));
|
||||
await execute(makeContext(root, { config: { cwd: root, model, effort: "high" } }));
|
||||
|
||||
expect(seenEnv.KIMI_MODEL_THINKING_EFFORT).toBe("high");
|
||||
});
|
||||
@@ -368,7 +368,7 @@ describe("kimi_local execute", () => {
|
||||
});
|
||||
|
||||
await execute(makeContext(root, {
|
||||
config: { cwd: root, model: "kimi-code/kimi-for-coding", effort: "high" },
|
||||
config: { cwd: root, model: "kimi-code/kimi-for-coding-highspeed", effort: "high" },
|
||||
}));
|
||||
|
||||
expect(seenEnv.KIMI_MODEL_THINKING_EFFORT).toBeUndefined();
|
||||
|
||||
@@ -53,6 +53,18 @@ export function isValidOpenCodeModelId(value: unknown): value is string {
|
||||
|
||||
export const models: Array<{ id: string; label: string }> = [
|
||||
{ id: DEFAULT_OPENCODE_LOCAL_MODEL, label: DEFAULT_OPENCODE_LOCAL_MODEL },
|
||||
{ id: "openai/gpt-6-astra", label: "openai/gpt-6-astra" },
|
||||
{ id: "openai/gpt-6-sol", label: "openai/gpt-6-sol" },
|
||||
{ id: "openai/gpt-6-luna", label: "openai/gpt-6-luna" },
|
||||
{ id: "openai/gpt-5.6-sol", label: "openai/gpt-5.6-sol" },
|
||||
{ id: "openai/gpt-5.6-terra", label: "openai/gpt-5.6-terra" },
|
||||
{ id: "openai/gpt-5.6-luna", label: "openai/gpt-5.6-luna" },
|
||||
{ id: "anthropic/claude-opus-5-5", label: "anthropic/claude-opus-5-5" },
|
||||
{ id: "anthropic/claude-opus-5", label: "anthropic/claude-opus-5" },
|
||||
{ id: "anthropic/claude-fable-5-1", label: "anthropic/claude-fable-5-1" },
|
||||
{ id: "anthropic/claude-sonnet-5", label: "anthropic/claude-sonnet-5" },
|
||||
{ id: "google/gemini-3.8-flash", label: "google/gemini-3.8-flash" },
|
||||
{ id: "xai/grok-4.7", label: "xai/grok-4.7" },
|
||||
{ id: "openai/gpt-5.5", label: "openai/gpt-5.5" },
|
||||
{ id: "openai/gpt-5.4", label: "openai/gpt-5.4" },
|
||||
{ id: "openai/gpt-5.4-mini", label: "openai/gpt-5.4-mini" },
|
||||
|
||||
Reference in new issue
Block a user