From 326c2b39748b99046cefbf83d1b7332a53e784a1 Mon Sep 17 00:00:00 2001 From: coach007 <6238600+keeperxy@users.noreply.github.com> Date: Fri, 23 Oct 2026 21:18:20 +0200 Subject: [PATCH 1/2] fix(web): honor scheduled task time format and expose reasoning options --- .../settings/ScheduledTasksSettings.tsx | 177 ++++++++++++++---- .../scheduledTasksSettings.logic.test.ts | 78 ++++++++ .../settings/scheduledTasksSettings.logic.ts | 34 +++- 3 files changed, 250 insertions(+), 39 deletions(-) diff --git a/apps/web/src/components/settings/ScheduledTasksSettings.tsx b/apps/web/src/components/settings/ScheduledTasksSettings.tsx index afaab157451e8..eecd88204479e 100644 --- a/apps/web/src/components/settings/ScheduledTasksSettings.tsx +++ b/apps/web/src/components/settings/ScheduledTasksSettings.tsx @@ -45,7 +45,7 @@ import { squashAtomCommandFailure, } from "@t3tools/client-runtime/state/runtime"; -import { formatRelativeTime } from "../../timestampFormat"; +import { formatRelativeTime, resolveHour12 } from "../../timestampFormat"; import { useEnvironmentSettings } from "../../hooks/useSettings"; import { getCustomModelOptionsByInstance } from "../../modelSelection"; import { @@ -83,6 +83,9 @@ import { webhookDraftErrors, webhookDeliveryLabel, matchesScheduledTaskScope, + formatScheduledTaskTimeInput, + parseScheduledTaskTimeInput, + resolveScheduledTaskModelSelection, scheduleFromDraft, scheduledTaskDefaultModel, taskToDraft, @@ -95,6 +98,7 @@ import { Menu, MenuTrigger, MenuPopup, MenuItem, MenuSeparator } from "../ui/men import { ToggleGroup, Toggle } from "../ui/toggle-group"; import { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyMedia } from "../ui/empty"; import { ProviderModelPicker } from "../chat/ProviderModelPicker"; +import { TraitsPicker } from "../chat/TraitsPicker"; import { Badge } from "../ui/badge"; import { Button } from "../ui/button"; import { @@ -108,6 +112,7 @@ import { DialogTitle, } from "../ui/dialog"; import { Input } from "../ui/input"; +import { InputGroup, InputGroupAddon, InputGroupInput } from "../ui/input-group"; import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select"; import { Switch } from "../ui/switch"; import { Textarea } from "../ui/textarea"; @@ -1127,6 +1132,22 @@ function ScheduledTaskEditorForm({ const [draft, setDraft] = useState(() => task ? taskToDraft(task) : { ...EMPTY_DRAFT, projectId: projects[0]?.id ?? "" }, ); + const [timeInput, setTimeInput] = useState<{ + readonly hour12: boolean; + readonly value: string; + readonly showError: boolean; + } | null>(null); + const hour12 = resolveHour12(settings.timestampFormat); + const displayedTime = + timeInput?.hour12 === hour12 + ? timeInput.value + : formatScheduledTaskTimeInput(draft.timeOfDay, hour12); + const parsedTime = parseScheduledTaskTimeInput(displayedTime, hour12); + const invalidTime = draft.scheduleMode === "fixed" && parsedTime === null; + const showTimeError = invalidTime && timeInput?.showError; + const timeError = hour12 + ? "Enter a time with AM or PM, such as 05:30 PM." + : "Enter a time from 00:00 to 23:59."; const submissionPending = useRef(false); const editingTaskMissing = draft.editingId !== null && @@ -1147,14 +1168,19 @@ function ScheduledTaskEditorForm({ const firstInstance = instanceEntries[0]; const draftSelection = draft.modelKey ? splitModelKey(draft.modelKey) : null; - const activeSelection = + const pickedSelection = draftSelection && instanceEntries.some((entry) => entry.instanceId === draftSelection.instanceId && entry.enabled) ? draftSelection : scheduledTaskDefaultModel(settings, selectedProject ?? null, instanceEntries); + const activeSelection = resolveScheduledTaskModelSelection( + pickedSelection, + draft.baseModelSelection, + ); const activeInstanceId = activeSelection?.instanceId ?? firstInstance?.instanceId ?? ("" as ProviderInstanceId); const activeModel = activeSelection?.model ?? ""; + const activeEntry = instanceEntries.find((entry) => entry.instanceId === activeInstanceId); const modelOptionsByInstance = getCustomModelOptionsByInstance( settings, providers, @@ -1197,7 +1223,11 @@ function ScheduledTaskEditorForm({ reportFailure("Invalid filter", parsedFilters.error); return; } - const schedule = scheduleFromDraft(draft); + if (invalidTime) { + reportFailure("Invalid time", timeError); + return; + } + const schedule = scheduleFromDraft({ ...draft, timeOfDay: parsedTime ?? draft.timeOfDay }); if (schedule === null) { for (const error of webhookDraftErrors(draft)) reportFailure(error.title, error.description); return; @@ -1223,12 +1253,6 @@ function ScheduledTaskEditorForm({ reportFailure("Checkout path is required", "Enter the path of the checkout to run in."); return; } - const modelSelection = - draft.baseModelSelection !== null && - draft.baseModelSelection.instanceId === selection.instanceId && - draft.baseModelSelection.model === selection.model - ? draft.baseModelSelection - : selection; const workspaceStrategy: OrchestrationV2ThreadLaunchWorkspaceStrategy = draft.workspaceMode === "root" ? { type: "root" } @@ -1248,7 +1272,7 @@ function ScheduledTaskEditorForm({ projectId: selectedProjectId as ProjectId, threadId: draft.threadId ? (draft.threadId as ThreadId) : null, workspaceStrategy, - modelSelection, + modelSelection: selection, runtimeMode: draft.runtimeMode, interactionMode: draft.interactionMode, creationSource: "web", @@ -1462,22 +1486,57 @@ function ScheduledTaskEditorForm({ -
- - setDraft((current) => ({ ...current, modelKey: `${instanceId}:${model}` })) - } - /> -
+ {draft.threadId ? ( +

+ Uses the linked thread's current model and reasoning settings. +

+ ) : ( +
+ + setDraft((current) => ({ + ...current, + modelKey: `${instanceId}:${model}`, + baseModelSelection: + activeSelection?.instanceId === instanceId && + activeSelection.model === model + ? activeSelection + : null, + })) + } + /> + {activeEntry && activeSelection ? ( + + setDraft((current) => ({ + ...current, + modelKey: `${activeSelection.instanceId}:${activeSelection.model}`, + baseModelSelection: { ...activeSelection, options: options ?? [] }, + })) + } + /> + ) : null} +
+ )}
@@ -1687,16 +1746,44 @@ function ScheduledTaskEditorForm({
- - setDraft((current) => ({ ...current, timeOfDay: event.target.value })) - } - /> +
+ + { + const value = event.target.value; + setTimeInput({ hour12, value, showError: false }); + const timeOfDay = parseScheduledTaskTimeInput(value, hour12); + if (timeOfDay !== null) + setDraft((current) => ({ ...current, timeOfDay })); + }} + onBlur={(event) => { + const timeOfDay = parseScheduledTaskTimeInput(event.target.value, hour12); + if (timeOfDay === null) { + setTimeInput({ hour12, value: event.target.value, showError: true }); + return; + } + setDraft((current) => ({ ...current, timeOfDay })); + setTimeInput(null); + }} + onKeyDown={(event) => { + if (event.key === "Enter") event.currentTarget.blur(); + }} + /> + + + + +
on
))} + {showTimeError ? ( + + ) : null}
) : (
@@ -1763,7 +1859,14 @@ function ScheduledTaskEditorForm({