diff --git a/apps/desktop/src/main/todo-agent/schedule-store.ts b/apps/desktop/src/main/todo-agent/schedule-store.ts index b4e905911df..1f4692503bb 100644 --- a/apps/desktop/src/main/todo-agent/schedule-store.ts +++ b/apps/desktop/src/main/todo-agent/schedule-store.ts @@ -71,6 +71,8 @@ class TodoScheduleStore { maxIterations: input.maxIterations, maxWallClockSec: input.maxWallClockSec, customSystemPrompt: input.customSystemPrompt ?? null, + claudeModel: input.claudeModel ?? null, + claudeEffort: input.claudeEffort ?? null, overlapMode: input.overlapMode, autoSyncBeforeFire: input.autoSyncBeforeFire, nextRunAt: input.nextRunAt, @@ -103,6 +105,10 @@ class TodoScheduleStore { patch.maxWallClockSec = rest.maxWallClockSec; if (rest.customSystemPrompt !== undefined) patch.customSystemPrompt = rest.customSystemPrompt ?? null; + if (rest.claudeModel !== undefined) + patch.claudeModel = rest.claudeModel ?? null; + if (rest.claudeEffort !== undefined) + patch.claudeEffort = rest.claudeEffort ?? null; if (rest.overlapMode !== undefined) patch.overlapMode = rest.overlapMode; if (rest.autoSyncBeforeFire !== undefined) patch.autoSyncBeforeFire = rest.autoSyncBeforeFire; diff --git a/apps/desktop/src/main/todo-agent/scheduler.ts b/apps/desktop/src/main/todo-agent/scheduler.ts index e817ed81b42..f59e2cd4aae 100644 --- a/apps/desktop/src/main/todo-agent/scheduler.ts +++ b/apps/desktop/src/main/todo-agent/scheduler.ts @@ -365,6 +365,8 @@ class TodoScheduler { maxIterations: schedule.maxIterations, maxWallClockSec: schedule.maxWallClockSec, customSystemPrompt: schedule.customSystemPrompt, + claudeModel: schedule.claudeModel, + claudeEffort: schedule.claudeEffort, artifactPath, }); supervisor.prepareArtifacts(inserted); diff --git a/apps/desktop/src/main/todo-agent/session-store.ts b/apps/desktop/src/main/todo-agent/session-store.ts index 6362ec30aba..a3baf80b0e5 100644 --- a/apps/desktop/src/main/todo-agent/session-store.ts +++ b/apps/desktop/src/main/todo-agent/session-store.ts @@ -251,6 +251,8 @@ class TodoSessionStore { maxIterations: number; maxWallClockSec: number; customSystemPrompt?: string | null; + claudeModel?: string | null; + claudeEffort?: string | null; artifactPath: string; }): SelectTodoSession { return this.insert({ @@ -275,6 +277,8 @@ class TodoSessionStore { pendingIntervention: null, startHeadSha: null, customSystemPrompt: template.customSystemPrompt ?? null, + claudeModel: template.claudeModel ?? null, + claudeEffort: template.claudeEffort ?? null, verdictPassed: null, verdictReason: null, verdictFailingTest: null, diff --git a/apps/desktop/src/main/todo-agent/settings.ts b/apps/desktop/src/main/todo-agent/settings.ts index 62dcafeabf8..9a15f0cd898 100644 --- a/apps/desktop/src/main/todo-agent/settings.ts +++ b/apps/desktop/src/main/todo-agent/settings.ts @@ -16,6 +16,8 @@ const DEFAULT_SETTINGS: TodoSettings = { defaultMaxWallClockMin: 30, maxConcurrentTasks: 1, sessionRetentionDays: 0, + defaultClaudeModel: null, + defaultClaudeEffort: null, }; let cached: TodoSettings | null = null; diff --git a/apps/desktop/src/main/todo-agent/supervisor.ts b/apps/desktop/src/main/todo-agent/supervisor.ts index e5b69f5a6e3..90a3ab5b360 100644 --- a/apps/desktop/src/main/todo-agent/supervisor.ts +++ b/apps/desktop/src/main/todo-agent/supervisor.ts @@ -7,7 +7,11 @@ import { getCurrentHeadSha } from "./git-status"; import { getTodoSessionStore, resolveWorktreePath } from "./session-store"; import { getTodoSettings } from "./settings"; import type { TodoStreamEventKind } from "./types"; -import { TODO_ARTIFACT_SUBDIR } from "./types"; +import { + CLAUDE_EFFORT_OPTIONS, + CLAUDE_MODEL_OPTIONS, + TODO_ARTIFACT_SUBDIR, +} from "./types"; /** * Headless Claude Code driver for TODO autonomous sessions. @@ -318,6 +322,13 @@ class TodoSupervisor { `${preview}${session0.customSystemPrompt.trim().length > 200 ? "…" : ""}`, ); } + if (session0.claudeModel || session0.claudeEffort) { + const parts: string[] = []; + if (session0.claudeModel) parts.push(`model: ${session0.claudeModel}`); + if (session0.claudeEffort) + parts.push(`effort: ${session0.claudeEffort}`); + appendSetupEvent(sessionId, "Claude 設定", parts.join(" / ")); + } appendSetupEvent( sessionId, "Claude", @@ -436,6 +447,8 @@ class TodoSupervisor { prompt, resumeSessionId: claudeSessionId, customSystemPrompt: currentSession.customSystemPrompt ?? null, + claudeModel: currentSession.claudeModel ?? null, + claudeEffort: currentSession.claudeEffort ?? null, signal: ac.signal, onChild: (child) => { run.currentChild = child; @@ -663,6 +676,8 @@ class TodoSupervisor { prompt: string; resumeSessionId: string | null; customSystemPrompt: string | null; + claudeModel: string | null; + claudeEffort: string | null; signal: AbortSignal; onChild: (child: ChildProcess) => void; }): Promise<{ @@ -704,6 +719,40 @@ class TodoSupervisor { if (params.customSystemPrompt) { args.push("--append-system-prompt", params.customSystemPrompt); } + // Per-session Claude Code overrides. Passing `--model` / + // `--effort` only when set keeps Claude Code's own default + // resolution path intact for users who haven't picked one. + // + // Defense-in-depth whitelist: the UI already constrains + // values via `CLAUDE_*_OPTIONS`, but that validation happens + // on the render side. A corrupted / migrated row could still + // persist an unexpected string. We refuse to forward anything + // that isn't in the allow-list so the spawn call can't be + // steered by a malformed DB value. + if ( + params.claudeModel && + (CLAUDE_MODEL_OPTIONS as readonly string[]).includes(params.claudeModel) + ) { + args.push("--model", params.claudeModel); + } else if (params.claudeModel) { + console.warn( + "[todo-supervisor] ignoring unknown claudeModel:", + params.claudeModel, + ); + } + if ( + params.claudeEffort && + (CLAUDE_EFFORT_OPTIONS as readonly string[]).includes( + params.claudeEffort, + ) + ) { + args.push("--effort", params.claudeEffort); + } else if (params.claudeEffort) { + console.warn( + "[todo-supervisor] ignoring unknown claudeEffort:", + params.claudeEffort, + ); + } if (params.resumeSessionId) { args.push("--resume", params.resumeSessionId); } diff --git a/apps/desktop/src/main/todo-agent/trpc-router.ts b/apps/desktop/src/main/todo-agent/trpc-router.ts index 8b66b88480f..3f1320109b7 100644 --- a/apps/desktop/src/main/todo-agent/trpc-router.ts +++ b/apps/desktop/src/main/todo-agent/trpc-router.ts @@ -103,6 +103,20 @@ export const createTodoAgentRouter = () => { workspaceId: input.workspaceId, }); + // Fall through to the user's configured defaults when the + // composer did not pick an explicit model / effort. Null + // at both levels means "use Claude Code's own default" + // (we simply omit the CLI flag at spawn time). + const settings = getTodoSettings(); + const resolvedModel = + input.claudeModel !== undefined + ? input.claudeModel + : (settings.defaultClaudeModel ?? null); + const resolvedEffort = + input.claudeEffort !== undefined + ? input.claudeEffort + : (settings.defaultClaudeEffort ?? null); + const session = store.insertQueuedFromTemplate({ id: sessionId, projectId: input.projectId ?? null, @@ -114,6 +128,8 @@ export const createTodoAgentRouter = () => { maxIterations: input.maxIterations, maxWallClockSec: input.maxWallClockSec, customSystemPrompt: input.customSystemPrompt, + claudeModel: resolvedModel, + claudeEffort: resolvedEffort, artifactPath, }); @@ -391,6 +407,8 @@ export const createTodoAgentRouter = () => { pendingIntervention: null, startHeadSha: null, customSystemPrompt: source.customSystemPrompt, + claudeModel: source.claudeModel, + claudeEffort: source.claudeEffort, verdictPassed: null, verdictReason: null, verdictFailingTest: null, diff --git a/apps/desktop/src/main/todo-agent/types.ts b/apps/desktop/src/main/todo-agent/types.ts index cce6473b15e..dcadc1b8fc1 100644 --- a/apps/desktop/src/main/todo-agent/types.ts +++ b/apps/desktop/src/main/todo-agent/types.ts @@ -17,6 +17,50 @@ export interface TodoSessionListEntry extends SelectTodoSession { projectName: string | null; } +/** + * Claude Code `--model` values we allow the user to pick from the UI. + * Aliases cover "latest of this tier"; full model names pin a specific + * release. Kept open-ended (plus a default `null` in the storage layer) + * so new models do not require a migration. `default` is the UI-side + * sentinel that maps to `null` (don't pass `--model` at all; let Claude + * Code use whatever the user's own config / ~/.claude.json chose). + */ +export const CLAUDE_MODEL_OPTIONS = [ + "opus", + "sonnet", + "haiku", + "claude-opus-4-7", + "claude-sonnet-4-6", + "claude-haiku-4-5-20251001", +] as const; + +export type TodoClaudeModel = (typeof CLAUDE_MODEL_OPTIONS)[number]; + +export const todoClaudeModelSchema = z.enum(CLAUDE_MODEL_OPTIONS); + +/** + * Claude Code `--effort` levels. `default` is the UI-side sentinel for + * "don't pass the flag"; actual persisted values are `low`..`max` or + * null. + * + * Thinking support is model-gated in Claude Code; the CLI rejects an + * incompatible effort level at launch. We intentionally don't duplicate + * that matrix here so adding a new model tier on the CLI side doesn't + * require a fork update. The UI surfaces a warning but allows the + * combination; the supervisor forwards whatever the user picked. + */ +export const CLAUDE_EFFORT_OPTIONS = [ + "low", + "medium", + "high", + "xhigh", + "max", +] as const; + +export type TodoClaudeEffort = (typeof CLAUDE_EFFORT_OPTIONS)[number]; + +export const todoClaudeEffortSchema = z.enum(CLAUDE_EFFORT_OPTIONS); + export const todoCreateInputSchema = z.object({ workspaceId: z.string().min(1), projectId: z.string().optional(), @@ -57,6 +101,10 @@ export const todoCreateInputSchema = z.object({ .max(20_000) .optional() .transform((v) => (v && v.length > 0 ? v : undefined)), + // Optional per-session Claude Code CLI overrides. Null / undefined + // means "use the user's configured default" (see todoSettingsSchema). + claudeModel: todoClaudeModelSchema.nullish(), + claudeEffort: todoClaudeEffortSchema.nullish(), }); export const todoPresetKindSchema = z.enum(["system", "description", "goal"]); @@ -93,6 +141,12 @@ export const todoSettingsSchema = z.object({ // 0 = 無制限 (手動削除のみ). 1-365 = その日数より古い終了済み // セッションを起動時に自動削除する (queued / running / paused は対象外)。 sessionRetentionDays: z.number().int().min(0).max(365).default(0), + // Global defaults used when the TODO composer / ScheduleEditor does + // not override them. Null = let Claude Code resolve its own default + // (user config cascade). Stored as nullable so the user can pick + // "default" in the settings UI. + defaultClaudeModel: todoClaudeModelSchema.nullish().default(null), + defaultClaudeEffort: todoClaudeEffortSchema.nullish().default(null), }); export type TodoSettings = z.infer; @@ -236,6 +290,8 @@ export const todoScheduleCreateInputSchema = z .max(60 * 60 * 4) .default(1800), customSystemPrompt: z.string().trim().max(20_000).nullish(), + claudeModel: todoClaudeModelSchema.nullish(), + claudeEffort: todoClaudeEffortSchema.nullish(), overlapMode: todoScheduleOverlapModeSchema.default("skip"), autoSyncBeforeFire: z.boolean().default(false), }) @@ -275,6 +331,8 @@ const todoScheduleBaseSchema = z.object({ .min(60) .max(60 * 60 * 4), customSystemPrompt: z.string().trim().max(20_000).nullish(), + claudeModel: todoClaudeModelSchema.nullish(), + claudeEffort: todoClaudeEffortSchema.nullish(), overlapMode: todoScheduleOverlapModeSchema, autoSyncBeforeFire: z.boolean(), }); diff --git a/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/ClaudeRuntimePicker.tsx b/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/ClaudeRuntimePicker.tsx new file mode 100644 index 00000000000..204715b40d9 --- /dev/null +++ b/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/ClaudeRuntimePicker.tsx @@ -0,0 +1,128 @@ +import { Label } from "@superset/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@superset/ui/select"; +import { cn } from "@superset/ui/utils"; +import { + CLAUDE_EFFORT_SELECT_OPTIONS, + CLAUDE_MODEL_SELECT_OPTIONS, + type ClaudeEffortPick, + type ClaudeModelPick, +} from "./claudeRuntimeOptions"; + +interface ClaudeRuntimePickerProps { + model: ClaudeModelPick; + effort: ClaudeEffortPick; + onModelChange: (value: ClaudeModelPick) => void; + onEffortChange: (value: ClaudeEffortPick) => void; + disabled?: boolean; + /** + * Layout variant. `stacked` labels above, `row` puts model + effort + * side by side. Defaults to `row`. + */ + layout?: "stacked" | "row"; + /** + * Shows the "デフォルト = 〜" hint line under the row. Hidden when the + * caller has its own explanation nearby (e.g. the Settings tab). + */ + showHint?: boolean; + /** + * Compact mode shrinks the control height + label size so the picker + * slots into tight dialog grids. Default matches the TodoModal form + * density. + */ + compact?: boolean; +} + +/** + * Model + effort picker used by the TODO composer, the Schedule editor, + * and the global defaults tab of the preset dialog. The Select surface + * is shared so picking a new model / effort for a single TODO or for the + * global default uses the exact same controls (including localized + * labels and the "Claude Code の既定値" sentinel). + */ +export function ClaudeRuntimePicker({ + model, + effort, + onModelChange, + onEffortChange, + disabled, + layout = "row", + showHint = true, + compact = true, +}: ClaudeRuntimePickerProps) { + const labelClass = compact ? "text-xs" : "text-sm"; + const triggerClass = compact ? "h-8 text-xs" : ""; + + return ( +
+
+
+ + +
+
+ + +
+
+ {showHint && ( +

+ デフォルト は --model / --effort を渡さないため、CLI + 側の設定(ユーザ設定や既定値)が優先される。 モデルと effort + の組み合わせによっては Claude Code + が対応していない場合があり、その時はセッションが即座にエラー終了する。 +

+ )} +
+ ); +} diff --git a/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/claudeRuntimeOptions.ts b/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/claudeRuntimeOptions.ts new file mode 100644 index 00000000000..36dd7e975fd --- /dev/null +++ b/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/claudeRuntimeOptions.ts @@ -0,0 +1,163 @@ +import { + CLAUDE_EFFORT_OPTIONS, + CLAUDE_MODEL_OPTIONS, + type TodoClaudeEffort, + type TodoClaudeModel, +} from "main/todo-agent/types"; + +/** + * Sentinel string used in the model/effort Select to represent "no + * explicit choice — let Claude Code's own default cascade win". The + * Select value space can't hold `null`, so we round-trip through this + * sentinel and convert to/from `null` at the persistence boundary. + * + * `__default__` was chosen over the empty string because Radix's Select + * treats empty strings as "value missing", which disables the visual + * selection state and forces the placeholder to render instead of the + * "デフォルト" label we want to show. + */ +export const DEFAULT_SENTINEL = "__default__" as const; + +export type ClaudeModelPick = typeof DEFAULT_SENTINEL | TodoClaudeModel; +export type ClaudeEffortPick = typeof DEFAULT_SENTINEL | TodoClaudeEffort; + +interface Option { + value: V; + label: string; + description: string; +} + +export const CLAUDE_MODEL_SELECT_OPTIONS: ReadonlyArray< + Option +> = [ + { + value: DEFAULT_SENTINEL, + label: "デフォルト", + description: "Claude Code の設定をそのまま使う(--model を渡さない)", + }, + { + value: "opus", + label: "Opus(最新)", + description: "opus 系の最新モデルを alias で指定(Claude 4.x)", + }, + { + value: "sonnet", + label: "Sonnet(最新)", + description: "sonnet 系の最新モデル", + }, + { + value: "haiku", + label: "Haiku(最新)", + description: "haiku 系の最新モデル(軽量・高速)", + }, + { + value: "claude-opus-4-7", + label: "Opus 4.7(固定)", + description: "Opus を 4.7 に固定したいとき", + }, + { + value: "claude-sonnet-4-6", + label: "Sonnet 4.6(固定)", + description: "Sonnet を 4.6 に固定したいとき", + }, + { + value: "claude-haiku-4-5-20251001", + label: "Haiku 4.5(固定)", + description: "Haiku を 4.5 に固定したいとき", + }, +] as const; + +export const CLAUDE_EFFORT_SELECT_OPTIONS: ReadonlyArray< + Option +> = [ + { + value: DEFAULT_SENTINEL, + label: "デフォルト", + description: "Claude Code の既定値を尊重する", + }, + { + value: "low", + label: "low(軽量)", + description: "思考量を抑える。単純タスク向け", + }, + { + value: "medium", + label: "medium", + description: "中程度の思考量。バランス型", + }, + { + value: "high", + label: "high", + description: "深く考えさせたいとき", + }, + { + value: "xhigh", + label: "xhigh", + description: "最上位クラスの思考量", + }, + { + value: "max", + label: "max(最大)", + description: "上限まで思考。コストが高くなるので注意", + }, +] as const; + +/** + * Hoist both constants so importers don't have to pull them from the + * main-process types alongside UI-only helpers. + */ +export { + CLAUDE_EFFORT_OPTIONS, + CLAUDE_MODEL_OPTIONS, + type TodoClaudeEffort, + type TodoClaudeModel, +}; + +export function toPersistedModel( + pick: ClaudeModelPick, +): TodoClaudeModel | null { + return pick === DEFAULT_SENTINEL ? null : pick; +} + +export function toPersistedEffort( + pick: ClaudeEffortPick, +): TodoClaudeEffort | null { + return pick === DEFAULT_SENTINEL ? null : pick; +} + +/** + * Narrow a DB-side `string | null` back into the picker's discriminated + * value space. Unknown strings (persisted from an older build with a + * wider allowed set) fall back to the sentinel so the Select stays on + * "デフォルト" instead of rendering as empty. We log a warning so a + * silent data regression is at least visible in DevTools — users who + * had a now-retired model selected will notice the reset when they + * next save the TODO / schedule. + */ +export function fromPersistedModel( + persisted: string | null | undefined, +): ClaudeModelPick { + if (persisted == null) return DEFAULT_SENTINEL; + if ((CLAUDE_MODEL_OPTIONS as readonly string[]).includes(persisted)) { + return persisted as TodoClaudeModel; + } + console.warn( + "[ClaudeRuntimePicker] unknown persisted model, falling back to default:", + persisted, + ); + return DEFAULT_SENTINEL; +} + +export function fromPersistedEffort( + persisted: string | null | undefined, +): ClaudeEffortPick { + if (persisted == null) return DEFAULT_SENTINEL; + if ((CLAUDE_EFFORT_OPTIONS as readonly string[]).includes(persisted)) { + return persisted as TodoClaudeEffort; + } + console.warn( + "[ClaudeRuntimePicker] unknown persisted effort, falling back to default:", + persisted, + ); + return DEFAULT_SENTINEL; +} diff --git a/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/index.ts b/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/index.ts new file mode 100644 index 00000000000..b6ca323e93b --- /dev/null +++ b/apps/desktop/src/renderer/features/todo-agent/ClaudeRuntimePicker/index.ts @@ -0,0 +1,12 @@ +export { ClaudeRuntimePicker } from "./ClaudeRuntimePicker"; +export { + CLAUDE_EFFORT_SELECT_OPTIONS, + CLAUDE_MODEL_SELECT_OPTIONS, + type ClaudeEffortPick, + type ClaudeModelPick, + DEFAULT_SENTINEL, + fromPersistedEffort, + fromPersistedModel, + toPersistedEffort, + toPersistedModel, +} from "./claudeRuntimeOptions"; diff --git a/apps/desktop/src/renderer/features/todo-agent/TodoManager/PresetsDialog/PresetsDialog.tsx b/apps/desktop/src/renderer/features/todo-agent/TodoManager/PresetsDialog/PresetsDialog.tsx index 9259d97f2d5..c01f5b76140 100644 --- a/apps/desktop/src/renderer/features/todo-agent/TodoManager/PresetsDialog/PresetsDialog.tsx +++ b/apps/desktop/src/renderer/features/todo-agent/TodoManager/PresetsDialog/PresetsDialog.tsx @@ -7,7 +7,7 @@ import { ScrollArea } from "@superset/ui/scroll-area"; import { toast } from "@superset/ui/sonner"; import { Textarea } from "@superset/ui/textarea"; import { cn } from "@superset/ui/utils"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { HiMiniCog6Tooth, HiMiniPlus, @@ -15,6 +15,16 @@ import { HiMiniXMark, } from "react-icons/hi2"; import { electronTrpc } from "renderer/lib/electron-trpc"; +import { + type ClaudeEffortPick, + type ClaudeModelPick, + ClaudeRuntimePicker, + DEFAULT_SENTINEL, + fromPersistedEffort, + fromPersistedModel, + toPersistedEffort, + toPersistedModel, +} from "../../ClaudeRuntimePicker"; interface PresetsDialogProps { open: boolean; @@ -101,14 +111,28 @@ function SettingsTab() { const [maxMin, setMaxMin] = useState(30); const [maxConcurrent, setMaxConcurrent] = useState(1); const [retentionDays, setRetentionDays] = useState(0); + const [defaultModel, setDefaultModel] = + useState(DEFAULT_SENTINEL); + const [defaultEffort, setDefaultEffort] = + useState(DEFAULT_SENTINEL); + // Hydrate form state the first time settings arrive from the main + // process. A React Query background refetch (window focus, etc.) + // re-fires the query even when no persisted data changed; without + // this guard it would silently clobber in-progress edits in the + // form, reverting the user's dirty state and erasing their changes + // the moment the window regained focus. + const hydratedRef = useRef(false); useEffect(() => { - if (settings) { - setMaxIter(settings.defaultMaxIterations); - setMaxMin(settings.defaultMaxWallClockMin); - setMaxConcurrent(settings.maxConcurrentTasks); - setRetentionDays(settings.sessionRetentionDays); - } + if (!settings) return; + if (hydratedRef.current) return; + setMaxIter(settings.defaultMaxIterations); + setMaxMin(settings.defaultMaxWallClockMin); + setMaxConcurrent(settings.maxConcurrentTasks); + setRetentionDays(settings.sessionRetentionDays); + setDefaultModel(fromPersistedModel(settings.defaultClaudeModel ?? null)); + setDefaultEffort(fromPersistedEffort(settings.defaultClaudeEffort ?? null)); + hydratedRef.current = true; }, [settings]); const dirty = @@ -116,7 +140,11 @@ function SettingsTab() { (maxIter !== settings.defaultMaxIterations || maxMin !== settings.defaultMaxWallClockMin || maxConcurrent !== settings.maxConcurrentTasks || - retentionDays !== settings.sessionRetentionDays); + retentionDays !== settings.sessionRetentionDays || + toPersistedModel(defaultModel) !== + (settings.defaultClaudeModel ?? null) || + toPersistedEffort(defaultEffort) !== + (settings.defaultClaudeEffort ?? null)); const handleSave = useCallback(async () => { try { @@ -125,6 +153,8 @@ function SettingsTab() { defaultMaxWallClockMin: maxMin, maxConcurrentTasks: maxConcurrent, sessionRetentionDays: retentionDays, + defaultClaudeModel: toPersistedModel(defaultModel), + defaultClaudeEffort: toPersistedEffort(defaultEffort), }); await utils.todoAgent.settings.get.invalidate(); toast.success("設定を保存しました"); @@ -133,7 +163,16 @@ function SettingsTab() { error instanceof Error ? error.message : "保存に失敗しました", ); } - }, [maxIter, maxMin, maxConcurrent, retentionDays, updateMut, utils]); + }, [ + defaultEffort, + defaultModel, + maxIter, + maxMin, + maxConcurrent, + retentionDays, + updateMut, + utils, + ]); return (
@@ -202,6 +241,21 @@ function SettingsTab() { で無効(手動削除のみ)。実行中・キュー中のセッションは対象外。

+
+ + +

+ 新規に作る TODO + やスケジュールのフォームに初期値として反映される。個別に上書き可。既存の + TODO / スケジュールには影響しない。 +

+
+ + diff --git a/apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx b/apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx index 95b8c8e70ce..443c48740bc 100644 --- a/apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx +++ b/apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx @@ -20,9 +20,19 @@ import { Label } from "@superset/ui/label"; import { toast } from "@superset/ui/sonner"; import { Textarea } from "@superset/ui/textarea"; import { cn } from "@superset/ui/utils"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { HiMiniSparkles, HiMiniXMark } from "react-icons/hi2"; import { electronTrpc } from "renderer/lib/electron-trpc"; +import { + type ClaudeEffortPick, + type ClaudeModelPick, + ClaudeRuntimePicker, + DEFAULT_SENTINEL, + fromPersistedEffort, + fromPersistedModel, + toPersistedEffort, + toPersistedModel, +} from "../ClaudeRuntimePicker"; import { EnhanceButton } from "./components/EnhanceButton"; interface TodoModalProps { @@ -63,6 +73,33 @@ export function TodoModal({ const [submitting, setSubmitting] = useState(false); const [createWorktree, setCreateWorktree] = useState(DEFAULT_CREATE_WORKTREE); const [selectedPresetId, setSelectedPresetId] = useState(null); + const [claudeModel, setClaudeModel] = + useState(DEFAULT_SENTINEL); + const [claudeEffort, setClaudeEffort] = + useState(DEFAULT_SENTINEL); + // Seed the picker from the global defaults only once per modal + // opening. Without this guard, a React Query background refetch or + // a settings update fired while the user is picking would overwrite + // a deliberate "デフォルト" (DEFAULT_SENTINEL) selection — flipping + // it back to the global default and silently changing what CLI flags + // the next run gets. + const seededRef = useRef(false); + useEffect(() => { + if (!open) { + seededRef.current = false; + return; + } + if (seededRef.current) return; + // Wait until the settings query resolves so we actually have a + // default to seed with. `todoSettings` is undefined on first + // render; seeding too early would lock us into the sentinel. + if (!todoSettings) return; + setClaudeModel(fromPersistedModel(todoSettings.defaultClaudeModel ?? null)); + setClaudeEffort( + fromPersistedEffort(todoSettings.defaultClaudeEffort ?? null), + ); + seededRef.current = true; + }, [open, todoSettings]); const utils = electronTrpc.useUtils(); const create = electronTrpc.todoAgent.create.useMutation({ @@ -93,6 +130,12 @@ export function TodoModal({ setMaxMinutes(todoSettings?.defaultMaxWallClockMin ?? DEFAULT_MAX_MINUTES); setCreateWorktree(DEFAULT_CREATE_WORKTREE); setSelectedPresetId(null); + setClaudeModel( + fromPersistedModel(todoSettings?.defaultClaudeModel ?? null), + ); + setClaudeEffort( + fromPersistedEffort(todoSettings?.defaultClaudeEffort ?? null), + ); setSubmitting(false); }, [todoSettings]); @@ -153,6 +196,8 @@ export function TodoModal({ maxIterations, maxWallClockSec: maxMinutes * 60, customSystemPrompt: selectedPreset?.content ?? undefined, + claudeModel: toPersistedModel(claudeModel), + claudeEffort: toPersistedEffort(claudeEffort), }); if (createWorktree) { toast.success( @@ -173,6 +218,8 @@ export function TodoModal({ hasGoal, hasVerify, canSubmit, + claudeEffort, + claudeModel, create, createWorkspaceMut, createWorktree, @@ -300,6 +347,14 @@ export function TodoModal({ /> + + {hasVerify && (
diff --git a/packages/local-db/drizzle/0058_todo_claude_model_effort.sql b/packages/local-db/drizzle/0058_todo_claude_model_effort.sql new file mode 100644 index 00000000000..f5dca07c718 --- /dev/null +++ b/packages/local-db/drizzle/0058_todo_claude_model_effort.sql @@ -0,0 +1,4 @@ +ALTER TABLE `todo_schedules` ADD `claude_model` text;--> statement-breakpoint +ALTER TABLE `todo_schedules` ADD `claude_effort` text;--> statement-breakpoint +ALTER TABLE `todo_sessions` ADD `claude_model` text;--> statement-breakpoint +ALTER TABLE `todo_sessions` ADD `claude_effort` text; \ No newline at end of file diff --git a/packages/local-db/drizzle/meta/0058_snapshot.json b/packages/local-db/drizzle/meta/0058_snapshot.json new file mode 100644 index 00000000000..28225373031 --- /dev/null +++ b/packages/local-db/drizzle/meta/0058_snapshot.json @@ -0,0 +1,2201 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "a8ed241a-41cd-4941-bc29-4e9ee916a858", + "prevId": "8306e7ba-5f4d-4168-a0e8-0e3f732f3cd3", + "tables": { + "browser_history": { + "name": "browser_history", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "favicon_url": { + "name": "favicon_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_visited_at": { + "name": "last_visited_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visit_count": { + "name": "visit_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": { + "browser_history_url_unique": { + "name": "browser_history_url_unique", + "columns": [ + "url" + ], + "isUnique": true + }, + "browser_history_url_idx": { + "name": "browser_history_url_idx", + "columns": [ + "url" + ], + "isUnique": false + }, + "browser_history_last_visited_at_idx": { + "name": "browser_history_last_visited_at_idx", + "columns": [ + "last_visited_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "browser_site_permissions": { + "name": "browser_site_permissions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "origin": { + "name": "origin", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ask'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "browser_site_permissions_origin_idx": { + "name": "browser_site_permissions_origin_idx", + "columns": [ + "origin" + ], + "isUnique": false + }, + "browser_site_permissions_origin_kind_unique": { + "name": "browser_site_permissions_origin_kind_unique", + "columns": [ + "origin", + "kind" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organization_members": { + "name": "organization_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organization_members_organization_id_idx": { + "name": "organization_members_organization_id_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "organization_members_user_id_idx": { + "name": "organization_members_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_user_id_users_id_fk": { + "name": "organization_members_user_id_users_id_fk", + "tableFrom": "organization_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "clerk_org_id": { + "name": "clerk_org_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_org": { + "name": "github_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organizations_clerk_org_id_unique": { + "name": "organizations_clerk_org_id_unique", + "columns": [ + "clerk_org_id" + ], + "isUnique": true + }, + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "organizations_slug_idx": { + "name": "organizations_slug_idx", + "columns": [ + "slug" + ], + "isUnique": false + }, + "organizations_clerk_org_id_idx": { + "name": "organizations_clerk_org_id_idx", + "columns": [ + "clerk_org_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "projects": { + "name": "projects", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "main_repo_path": { + "name": "main_repo_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tab_order": { + "name": "tab_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_opened_at": { + "name": "last_opened_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_toast_dismissed": { + "name": "config_toast_dismissed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "workspace_base_branch": { + "name": "workspace_base_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_owner": { + "name": "github_owner", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_mode": { + "name": "branch_prefix_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_custom": { + "name": "branch_prefix_custom", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "worktree_base_dir": { + "name": "worktree_base_dir", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hide_image": { + "name": "hide_image", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "neon_project_id": { + "name": "neon_project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_app": { + "name": "default_app", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "projects_main_repo_path_idx": { + "name": "projects_main_repo_path_idx", + "columns": [ + "main_repo_path" + ], + "isUnique": false + }, + "projects_last_opened_at_idx": { + "name": "projects_last_opened_at_idx", + "columns": [ + "last_opened_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "settings": { + "name": "settings", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "last_active_workspace_id": { + "name": "last_active_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_presets": { + "name": "terminal_presets", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_presets_initialized": { + "name": "terminal_presets_initialized", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_preset_overrides": { + "name": "agent_preset_overrides", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_custom_definitions": { + "name": "agent_custom_definitions", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "selected_ringtone_id": { + "name": "selected_ringtone_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "confirm_on_quit": { + "name": "confirm_on_quit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_link_behavior": { + "name": "terminal_link_behavior", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "persist_terminal": { + "name": "persist_terminal", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": true + }, + "auto_apply_default_preset": { + "name": "auto_apply_default_preset", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_mode": { + "name": "branch_prefix_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_custom": { + "name": "branch_prefix_custom", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_sounds_muted": { + "name": "notification_sounds_muted", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_volume": { + "name": "notification_volume", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "prevent_agent_sleep": { + "name": "prevent_agent_sleep", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delete_local_branch": { + "name": "delete_local_branch", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "file_open_mode": { + "name": "file_open_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "right_sidebar_open_view_width": { + "name": "right_sidebar_open_view_width", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "show_presets_bar": { + "name": "show_presets_bar", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "use_compact_terminal_add_button": { + "name": "use_compact_terminal_add_button", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_font_family": { + "name": "terminal_font_family", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_font_size": { + "name": "terminal_font_size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "editor_font_family": { + "name": "editor_font_family", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "editor_font_size": { + "name": "editor_font_size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "show_resource_monitor": { + "name": "show_resource_monitor", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "worktree_base_dir": { + "name": "worktree_base_dir", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "open_links_in_app": { + "name": "open_links_in_app", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_editor": { + "name": "default_editor", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "indent_rainbow_enabled": { + "name": "indent_rainbow_enabled", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "indent_rainbow_colors": { + "name": "indent_rainbow_colors", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trailing_spaces_enabled": { + "name": "trailing_spaces_enabled", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trailing_spaces_color": { + "name": "trailing_spaces_color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_graph_enabled": { + "name": "reference_graph_enabled", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expose_host_service_via_relay": { + "name": "expose_host_service_via_relay", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "enable_smart_commit": { + "name": "enable_smart_commit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "smart_commit_changes": { + "name": "smart_commit_changes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "auto_stash": { + "name": "auto_stash", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_sort_order": { + "name": "branch_sort_order", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pin_default_branch": { + "name": "pin_default_branch", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "post_commit_command": { + "name": "post_commit_command", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tasks": { + "name": "tasks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_color": { + "name": "status_color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_type": { + "name": "status_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_position": { + "name": "status_position", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "assignee_id": { + "name": "assignee_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "estimate": { + "name": "estimate", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "labels": { + "name": "labels", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_provider": { + "name": "external_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_key": { + "name": "external_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_url": { + "name": "external_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sync_error": { + "name": "sync_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tasks_slug_unique": { + "name": "tasks_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "tasks_slug_idx": { + "name": "tasks_slug_idx", + "columns": [ + "slug" + ], + "isUnique": false + }, + "tasks_organization_id_idx": { + "name": "tasks_organization_id_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "tasks_assignee_id_idx": { + "name": "tasks_assignee_id_idx", + "columns": [ + "assignee_id" + ], + "isUnique": false + }, + "tasks_status_idx": { + "name": "tasks_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "tasks_created_at_idx": { + "name": "tasks_created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "tasks_organization_id_organizations_id_fk": { + "name": "tasks_organization_id_organizations_id_fk", + "tableFrom": "tasks", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tasks_assignee_id_users_id_fk": { + "name": "tasks_assignee_id_users_id_fk", + "tableFrom": "tasks", + "tableTo": "users", + "columnsFrom": [ + "assignee_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tasks_creator_id_users_id_fk": { + "name": "tasks_creator_id_users_id_fk", + "tableFrom": "tasks", + "tableTo": "users", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "clerk_id": { + "name": "clerk_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "columns": [ + "clerk_id" + ], + "isUnique": true + }, + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + "email" + ], + "isUnique": false + }, + "users_clerk_id_idx": { + "name": "users_clerk_id_idx", + "columns": [ + "clerk_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workspace_sections": { + "name": "workspace_sections", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tab_order": { + "name": "tab_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_collapsed": { + "name": "is_collapsed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "workspace_sections_project_id_idx": { + "name": "workspace_sections_project_id_idx", + "columns": [ + "project_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspace_sections_project_id_projects_id_fk": { + "name": "workspace_sections_project_id_projects_id_fk", + "tableFrom": "workspace_sections", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workspaces": { + "name": "workspaces", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "worktree_id": { + "name": "worktree_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tab_order": { + "name": "tab_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_opened_at": { + "name": "last_opened_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_unread": { + "name": "is_unread", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "is_unnamed": { + "name": "is_unnamed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "deleting_at": { + "name": "deleting_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "port_base": { + "name": "port_base", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "section_id": { + "name": "section_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "workspaces_project_id_idx": { + "name": "workspaces_project_id_idx", + "columns": [ + "project_id" + ], + "isUnique": false + }, + "workspaces_worktree_id_idx": { + "name": "workspaces_worktree_id_idx", + "columns": [ + "worktree_id" + ], + "isUnique": false + }, + "workspaces_last_opened_at_idx": { + "name": "workspaces_last_opened_at_idx", + "columns": [ + "last_opened_at" + ], + "isUnique": false + }, + "workspaces_section_id_idx": { + "name": "workspaces_section_id_idx", + "columns": [ + "section_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspaces_project_id_projects_id_fk": { + "name": "workspaces_project_id_projects_id_fk", + "tableFrom": "workspaces", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspaces_worktree_id_worktrees_id_fk": { + "name": "workspaces_worktree_id_worktrees_id_fk", + "tableFrom": "workspaces", + "tableTo": "worktrees", + "columnsFrom": [ + "worktree_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspaces_section_id_workspace_sections_id_fk": { + "name": "workspaces_section_id_workspace_sections_id_fk", + "tableFrom": "workspaces", + "tableTo": "workspace_sections", + "columnsFrom": [ + "section_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "worktrees": { + "name": "worktrees", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "base_branch": { + "name": "base_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "git_status": { + "name": "git_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_status": { + "name": "github_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by_superset": { + "name": "created_by_superset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + } + }, + "indexes": { + "worktrees_project_id_idx": { + "name": "worktrees_project_id_idx", + "columns": [ + "project_id" + ], + "isUnique": false + }, + "worktrees_branch_idx": { + "name": "worktrees_branch_idx", + "columns": [ + "branch" + ], + "isUnique": false + } + }, + "foreignKeys": { + "worktrees_project_id_projects_id_fk": { + "name": "worktrees_project_id_projects_id_fk", + "tableFrom": "worktrees", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "todo_prompt_presets": { + "name": "todo_prompt_presets", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "todo_prompt_presets_name_idx": { + "name": "todo_prompt_presets_name_idx", + "columns": [ + "name" + ], + "isUnique": false + }, + "todo_prompt_presets_updated_at_idx": { + "name": "todo_prompt_presets_updated_at_idx", + "columns": [ + "updated_at" + ], + "isUnique": false + }, + "todo_prompt_presets_kind_idx": { + "name": "todo_prompt_presets_kind_idx", + "columns": [ + "kind" + ], + "isUnique": false + }, + "todo_prompt_presets_workspace_idx": { + "name": "todo_prompt_presets_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "todo_schedules": { + "name": "todo_schedules", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "frequency": { + "name": "frequency", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "minute": { + "name": "minute", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hour": { + "name": "hour", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "weekday": { + "name": "weekday", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "monthday": { + "name": "monthday", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cron_expr": { + "name": "cron_expr", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "goal": { + "name": "goal", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verify_command": { + "name": "verify_command", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "max_iterations": { + "name": "max_iterations", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "max_wall_clock_sec": { + "name": "max_wall_clock_sec", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1800 + }, + "custom_system_prompt": { + "name": "custom_system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claude_model": { + "name": "claude_model", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claude_effort": { + "name": "claude_effort", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "overlap_mode": { + "name": "overlap_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'skip'" + }, + "auto_sync_before_fire": { + "name": "auto_sync_before_fire", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "last_run_at": { + "name": "last_run_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_run_session_id": { + "name": "last_run_session_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "todo_schedules_project_idx": { + "name": "todo_schedules_project_idx", + "columns": [ + "project_id" + ], + "isUnique": false + }, + "todo_schedules_workspace_idx": { + "name": "todo_schedules_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "todo_schedules_enabled_next_run_idx": { + "name": "todo_schedules_enabled_next_run_idx", + "columns": [ + "enabled", + "next_run_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "todo_schedules_project_id_projects_id_fk": { + "name": "todo_schedules_project_id_projects_id_fk", + "tableFrom": "todo_schedules", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "todo_schedules_workspace_id_workspaces_id_fk": { + "name": "todo_schedules_workspace_id_workspaces_id_fk", + "tableFrom": "todo_schedules", + "tableTo": "workspaces", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "todo_sessions": { + "name": "todo_sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "goal": { + "name": "goal", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verify_command": { + "name": "verify_command", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "max_iterations": { + "name": "max_iterations", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "max_wall_clock_sec": { + "name": "max_wall_clock_sec", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1800 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'queued'" + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "iteration": { + "name": "iteration", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "attached_pane_id": { + "name": "attached_pane_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "attached_tab_id": { + "name": "attached_tab_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claude_session_id": { + "name": "claude_session_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "final_assistant_text": { + "name": "final_assistant_text", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_cost_usd": { + "name": "total_cost_usd", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_num_turns": { + "name": "total_num_turns", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pending_intervention": { + "name": "pending_intervention", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "start_head_sha": { + "name": "start_head_sha", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "custom_system_prompt": { + "name": "custom_system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claude_model": { + "name": "claude_model", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claude_effort": { + "name": "claude_effort", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verdict_passed": { + "name": "verdict_passed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verdict_reason": { + "name": "verdict_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verdict_failing_test": { + "name": "verdict_failing_test", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "artifact_path": { + "name": "artifact_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "waiting_until": { + "name": "waiting_until", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "waiting_reason": { + "name": "waiting_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "todo_sessions_workspace_idx": { + "name": "todo_sessions_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "todo_sessions_status_idx": { + "name": "todo_sessions_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "todo_sessions_created_at_idx": { + "name": "todo_sessions_created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "todo_sessions_project_id_projects_id_fk": { + "name": "todo_sessions_project_id_projects_id_fk", + "tableFrom": "todo_sessions", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "todo_sessions_workspace_id_workspaces_id_fk": { + "name": "todo_sessions_workspace_id_workspaces_id_fk", + "tableFrom": "todo_sessions", + "tableTo": "workspaces", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/local-db/drizzle/meta/_journal.json b/packages/local-db/drizzle/meta/_journal.json index 1088a31e3f8..bb3bb895d1c 100644 --- a/packages/local-db/drizzle/meta/_journal.json +++ b/packages/local-db/drizzle/meta/_journal.json @@ -407,6 +407,13 @@ "when": 1776362216006, "tag": "0057_todo_session_waiting", "breakpoints": true + }, + { + "idx": 58, + "version": "6", + "when": 1776378635928, + "tag": "0058_todo_claude_model_effort", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/local-db/src/schema/todo-schedules.ts b/packages/local-db/src/schema/todo-schedules.ts index 1a4a183abab..ff0f83121a3 100644 --- a/packages/local-db/src/schema/todo-schedules.ts +++ b/packages/local-db/src/schema/todo-schedules.ts @@ -55,6 +55,13 @@ export const todoSchedules = sqliteTable( maxWallClockSec: integer("max_wall_clock_sec").notNull().default(1800), customSystemPrompt: text("custom_system_prompt"), + // Optional Claude Code model + effort overrides applied to every + // session this schedule creates. Null = fall back to whatever the + // supervisor would pick for an unconfigured TODO (Claude Code's + // own default). + claudeModel: text("claude_model"), + claudeEffort: text("claude_effort"), + // How to behave when the previous session from this schedule is // still running at fire time. overlapMode: text("overlap_mode", { enum: ["skip", "queue"] }) diff --git a/packages/local-db/src/schema/todo-sessions.ts b/packages/local-db/src/schema/todo-sessions.ts index 0815a8c3874..0127eb51029 100644 --- a/packages/local-db/src/schema/todo-sessions.ts +++ b/packages/local-db/src/schema/todo-sessions.ts @@ -86,6 +86,17 @@ export const todoSessions = sqliteTable( // CLAUDE.md / workspace context already applies. customSystemPrompt: text("custom_system_prompt"), + // Optional per-session Claude Code model + effort overrides. When + // null, the supervisor omits the corresponding `--model` / + // `--effort` flag and lets Claude Code use its resolved default + // (user config + upstream default cascade). Values are free-form + // strings so we can persist either an alias (`opus`, `sonnet`) + // or a full model name (`claude-opus-4-7`) without a migration + // every time Anthropic ships a new tier. The UI constrains the + // allowed values. + claudeModel: text("claude_model"), + claudeEffort: text("claude_effort"), + verdictPassed: integer("verdict_passed", { mode: "boolean" }), verdictReason: text("verdict_reason"), verdictFailingTest: text("verdict_failing_test"),