Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 4 additions & 28 deletions src/renderer/components/providers/commandcode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,8 @@ import { registerTitleGenDefaults } from "../titleGen";
const PROVIDER_KIND = providerManifest.kind;

registerProviderIcon(PROVIDER_KIND, CommandCodeIcon);
// Command Code is usage-priced and gates Claude/GPT models behind paid plans:
// the previous defaults (gpt-5.4-mini, claude-sonnet-4-6, google/gemini-3.1-flash-
// lite) all return "403 MODEL_NOT_IN_PLAN" on the base plan, so Auto generation
// failed outright. Picks below are open-source models available on every plan.
// `deepseek-v4-flash` is Command Code's own default — the cheap, ungated baseline
// that a local benchmark confirmed is fast and types commits correctly; the
// "-Fast"/"-Highspeed" tiers (e.g. GLM-5.2-Fast) are quicker but cost more, so
// they're not worth it for frequent title/commit runs. `deepseek-v4-pro` adds
// reasoning for the rarer, heavier conflict resolver. Pro users can override.
registerCommitGenDefaults(PROVIDER_KIND, {
label: "Command Code",
hint: "DeepSeek V4 Flash",
model: "deepseek/deepseek-v4-flash",
effort: "",
});
registerTitleGenDefaults(PROVIDER_KIND, {
label: "Command Code",
hint: "DeepSeek V4 Flash",
model: "deepseek/deepseek-v4-flash",
effort: "",
});
registerConflictResolverDefaults(PROVIDER_KIND, {
label: "Command Code",
hint: "DeepSeek V4 Pro",
model: "deepseek/deepseek-v4-pro",
effort: "",
});

const liveDefaults = { label: "Command Code", model: "", effort: "" };
registerCommitGenDefaults(PROVIDER_KIND, liveDefaults);
registerTitleGenDefaults(PROVIDER_KIND, liveDefaults);
registerConflictResolverDefaults(PROVIDER_KIND, liveDefaults);
registerComposerControls(PROVIDER_KIND, (input) => standardPlanApprovalControls(input));
40 changes: 36 additions & 4 deletions src/renderer/state/agentStatusesStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,41 @@ function reset() {
beforeEach(reset);

describe("persisted agent status cache", () => {
it("invalidates v15 statuses cached before Command Code's live-only model discovery", async () => {
const options = useAgentStatusesStore.persist.getOptions();
expect(options.version).toBe(16);
const staleCommandCode = makeStatus({
kind: "commandcode",
label: "Command Code",
capabilities: {
...makeStatus().capabilities,
models: [
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro" },
{ id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash" },
],
modelSubProvider: { "deepseek/deepseek-v4-pro": "deepseek" },
},
});
const migrated = await options.migrate!(
{
agentStatuses: [staleCommandCode],
wslAgentStatuses: [],
windowsLoaded: true,
wslLoaded: true,
},
15,
);
expect(migrated).toMatchObject({
agentStatuses: [],
wslAgentStatuses: [],
windowsLoaded: false,
wslLoaded: false,
});
});

it("invalidates v10 statuses whose terminal auth methods lack baseSpawnEnv-derived env", async () => {
const options = useAgentStatusesStore.persist.getOptions();
expect(options.version).toBe(15);
expect(options.version).toBe(16);
const staleLogin = makeStatus({
kind: "antigravity",
label: "Antigravity",
Expand All @@ -74,7 +106,7 @@ describe("persisted agent status cache", () => {

it("invalidates v14 statuses that grouped Cursor Grok under Other models", async () => {
const options = useAgentStatusesStore.persist.getOptions();
expect(options.version).toBe(15);
expect(options.version).toBe(16);
const staleCursor = makeStatus({
kind: "cursor",
label: "Cursor",
Expand Down Expand Up @@ -107,7 +139,7 @@ describe("persisted agent status cache", () => {

it("invalidates v8 statuses cached before successful ACP sessions established auth", async () => {
const options = useAgentStatusesStore.persist.getOptions();
expect(options.version).toBe(15);
expect(options.version).toBe(16);
const staleAcp = makeStatus({
kind: "acp-generic:example",
label: "Example ACP",
Expand All @@ -134,7 +166,7 @@ describe("persisted agent status cache", () => {

it("invalidates v6 statuses produced without the Grok login-shell environment", async () => {
const options = useAgentStatusesStore.persist.getOptions();
expect(options.version).toBe(15);
expect(options.version).toBe(16);
expect(options.migrate).toBeTypeOf("function");

const grok = makeStatus({
Expand Down
7 changes: 4 additions & 3 deletions src/renderer/state/agentStatusesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,10 @@ export const useAgentStatusesStore = create<AgentStatusesStore>()(
}),
{
name: "poracode-agent-statuses-v1",
version: 15,
// v15 mirrors supervisor STATUS_CACHE_VERSION=18 so this localStorage
// copy does not keep Cursor Grok grouped under Other models.
version: 16,
// v16 mirrors supervisor STATUS_CACHE_VERSION=19 so this localStorage
// copy does not keep Command Code's pre-live-discovery curated model
// table (deepseek fallbacks, static `defaultEffort`) on first paint.
migrate: (persisted) => {
const prev = (persisted ?? {}) as Partial<AgentStatusesStore>;
return {
Expand Down
1 change: 1 addition & 0 deletions src/supervisor/agents/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export type {
} from "./types";
export * from "./terminalHints";
export * from "./expectedRuntimeError";
export * from "./oneShotModel";
export * from "./promptSession";
export * from "./processRuntime";
export * from "./shellBasics";
Expand Down
21 changes: 21 additions & 0 deletions src/supervisor/agents/base/oneShotModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { AgentAdapter } from "./types";

/**
* Resolve the model a utility one-shot should run with: the caller's explicit
* pick, else the adapter's default. An empty result is only allowed when the
* adapter opted into implicit models (`allowsImplicitOneShotModel`) — CLI
* adapters may then omit `--model` and use the target environment's own live
* default. `makeNoModelError` supplies the caller-facing error so each
* generator keeps its own localized message.
*/
export function resolveOneShotEffectiveModel(
adapter: Pick<AgentAdapter, "defaultOneShotModel" | "allowsImplicitOneShotModel" | "label">,
model: string | undefined,
makeNoModelError: () => Error,
): string {
const effectiveModel = model ?? adapter.defaultOneShotModel ?? "";
if (!effectiveModel && !adapter.allowsImplicitOneShotModel) {
throw makeNoModelError();
}
return effectiveModel;
}
2 changes: 2 additions & 0 deletions src/supervisor/agents/base/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,8 @@ export interface OneShotGenerationOptions {

export interface AgentOneShotRunner {
defaultOneShotModel?: string;
/** Allow CLI adapters to omit `--model` and use the target environment's own live default. */
allowsImplicitOneShotModel?: boolean;
/**
* Build a bypass-permissions CLI invocation so an agent WITHOUT a structured
* (GUI) runtime can still be spawned as a one-shot subagent child. Implemented
Expand Down
Loading