From 0e47b388d5a0096b635a635ee6c78d112ef58411 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Mon, 20 Jul 2026 23:07:01 +0300 Subject: [PATCH] feat: scheduled task permission auto-accept and composer-style editor toggles --- .../session/ScheduledTaskEditorDialog.tsx | 77 ++++++++++++++----- .../session/ScheduledTasksDialog.tsx | 9 ++- packages/ui/src/lib/i18n/messages/en.ts | 2 + packages/ui/src/lib/i18n/messages/es.ts | 2 + packages/ui/src/lib/i18n/messages/fr.ts | 2 + packages/ui/src/lib/i18n/messages/ja.ts | 2 + packages/ui/src/lib/i18n/messages/ko.ts | 2 + packages/ui/src/lib/i18n/messages/pl.ts | 2 + packages/ui/src/lib/i18n/messages/pt-BR.ts | 2 + packages/ui/src/lib/i18n/messages/uk.ts | 2 + packages/ui/src/lib/i18n/messages/zh-CN.ts | 2 + packages/ui/src/lib/i18n/messages/zh-TW.ts | 2 + packages/ui/src/lib/scheduledTasksApi.ts | 1 + packages/ui/src/lib/timezones.ts | 14 ++++ packages/web/server/index.js | 1 + .../web/server/lib/projects/project-config.js | 2 + .../web/server/lib/scheduled-tasks/runtime.js | 12 +++ 17 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 packages/ui/src/lib/timezones.ts diff --git a/packages/ui/src/components/session/ScheduledTaskEditorDialog.tsx b/packages/ui/src/components/session/ScheduledTaskEditorDialog.tsx index def077c3..10d0b2b3 100644 --- a/packages/ui/src/components/session/ScheduledTaskEditorDialog.tsx +++ b/packages/ui/src/components/session/ScheduledTaskEditorDialog.tsx @@ -18,17 +18,23 @@ import { CommandAutocomplete, type CommandAutocompleteHandle, type CommandInfo } import { FileMentionAutocomplete, type FileMentionHandle } from '@/components/chat/FileMentionAutocomplete'; import { SnippetAutocomplete, type SnippetAutocompleteHandle } from '@/components/chat/SnippetAutocomplete'; import { Icon } from "@/components/icon/Icon"; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { useConfigStore } from '@/stores/useConfigStore'; import { useUIStore } from '@/stores/useUIStore'; import type { ScheduledTask } from '@/lib/scheduledTasksApi'; import { useI18n } from '@/lib/i18n'; import { isValidCronExpression, getNextRuns, CRON_EXAMPLES } from '@/lib/cron'; +import { canonicalizeTimezone } from '@/lib/timezones'; const WEEKDAY_INDEXES = [0, 1, 2, 3, 4, 5, 6] as const; +// Mirrors the composer's footer icon toggles (PermissionAutoAcceptButton / +// SessionGoalButton) so the state reads the same in both places. +const EDITOR_TOGGLE_BUTTON_CLASS = 'flex h-6 w-6 cursor-pointer items-center justify-center text-foreground transition-none outline-none focus:outline-none flex-shrink-0'; + const TIMEZONE_OPTIONS = (() => { if (typeof Intl !== 'undefined' && typeof Intl.supportedValuesOf === 'function') { - return Intl.supportedValuesOf('timeZone'); + return [...new Set(Intl.supportedValuesOf('timeZone').map(canonicalizeTimezone))]; } return [ 'UTC', @@ -470,6 +476,7 @@ type ScheduledTaskDraft = { agent: string; goalEnabled: boolean; goalTokenBudget: number | null; + permissionAutoAccept: boolean; }; state?: ScheduledTask['state']; }; @@ -499,7 +506,7 @@ const toDraft = ( agent: string; }, ): ScheduledTaskDraft => { - const timezoneFallback = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; + const timezoneFallback = canonicalizeTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'); if (!task) { return { name: '', @@ -521,6 +528,7 @@ const toDraft = ( agent: defaults.agent, goalEnabled: false, goalTokenBudget: null, + permissionAutoAccept: false, }, }; } @@ -543,7 +551,7 @@ const toDraft = ( ? task.schedule.time : '09:00', weekdays: Array.isArray(task.schedule.weekdays) ? task.schedule.weekdays : [1], - timezone: task.schedule.timezone || timezoneFallback, + timezone: canonicalizeTimezone(task.schedule.timezone || timezoneFallback), cronExpression: task.schedule.kind === 'cron' && typeof task.schedule.cron === 'string' ? task.schedule.cron : '', @@ -558,6 +566,7 @@ const toDraft = ( goalTokenBudget: typeof task.execution.goalTokenBudget === 'number' && task.execution.goalTokenBudget > 0 ? task.execution.goalTokenBudget : null, + permissionAutoAccept: task.execution.permissionAutoAccept === true, }, state: task.state, }; @@ -1163,6 +1172,7 @@ export function ScheduledTaskEditorDialog(props: { modelID: draft.execution.modelID, ...(draft.execution.variant.trim() ? { variant: draft.execution.variant.trim() } : {}), ...(draft.execution.agent.trim() ? { agent: draft.execution.agent.trim() } : {}), + ...(draft.execution.permissionAutoAccept ? { permissionAutoAccept: true } : {}), ...(draft.execution.goalEnabled ? { goalEnabled: true } : {}), ...(draft.execution.goalEnabled && draft.execution.goalTokenBudget ? { goalTokenBudget: draft.execution.goalTokenBudget } @@ -1187,7 +1197,7 @@ export function ScheduledTaskEditorDialog(props: { if (typeof document === 'undefined') return false; return Boolean( document.querySelector( - '[data-slot="dropdown-menu-content"], [data-slot="select-content"]' + '[data-slot="dropdown-menu-content"][data-open], [data-slot="select-content"][data-open]' ) ); }, []); @@ -1623,19 +1633,8 @@ export function ScheduledTaskEditorDialog(props: { + {draft.execution.goalEnabled ? (
- - {draft.execution.goalEnabled ? ( - ) : null} - {draft.execution.goalEnabled && draft.execution.goalTokenBudget !== null ? ( + {draft.execution.goalTokenBudget !== null ? ( { @@ -1665,6 +1663,7 @@ export function ScheduledTaskEditorDialog(props: { /> ) : null}
+ ) : null} ); @@ -1680,6 +1679,48 @@ export function ScheduledTaskEditorDialog(props: {
+ + + + + {t('sessions.scheduledTasks.editor.permissionAutoAccept.label')} + + + + + + {t('sessions.scheduledTasks.editor.goal.label')} + diff --git a/packages/ui/src/components/session/ScheduledTasksDialog.tsx b/packages/ui/src/components/session/ScheduledTasksDialog.tsx index 80589d66..0b1a0702 100644 --- a/packages/ui/src/components/session/ScheduledTasksDialog.tsx +++ b/packages/ui/src/components/session/ScheduledTasksDialog.tsx @@ -34,6 +34,7 @@ import { type ScheduledTaskStatus, } from '@/lib/scheduledTasksApi'; import { ScheduledTaskEditorDialog } from './ScheduledTaskEditorDialog'; +import { canonicalizeTimezone } from '@/lib/timezones'; const scheduleTimes = (task: ScheduledTask): string[] => { const raw = Array.isArray(task.schedule.times) @@ -59,7 +60,7 @@ const formatSchedule = (task: ScheduledTask, t: ReturnType['t']) if (task.schedule.timezone) { return t('sessions.scheduledTasks.dialog.schedule.dailyWithTimezone', { time: timesLabel, - timezone: task.schedule.timezone, + timezone: canonicalizeTimezone(task.schedule.timezone), }); } return t('sessions.scheduledTasks.dialog.schedule.daily', { time: timesLabel }); @@ -72,7 +73,7 @@ const formatSchedule = (task: ScheduledTask, t: ReturnType['t']) return t('sessions.scheduledTasks.dialog.schedule.weeklyWithTimezone', { days, time: timesLabel, - timezone: task.schedule.timezone, + timezone: canonicalizeTimezone(task.schedule.timezone), }); } return t('sessions.scheduledTasks.dialog.schedule.weekly', { days, time: timesLabel }); @@ -88,7 +89,7 @@ const formatSchedule = (task: ScheduledTask, t: ReturnType['t']) return t('sessions.scheduledTasks.dialog.schedule.onceWithTimezone', { date, time, - timezone: task.schedule.timezone, + timezone: canonicalizeTimezone(task.schedule.timezone), }); } return t('sessions.scheduledTasks.dialog.schedule.once', { date, time }); @@ -96,7 +97,7 @@ const formatSchedule = (task: ScheduledTask, t: ReturnType['t']) if (task.schedule.timezone) { return t('sessions.scheduledTasks.dialog.schedule.cronWithTimezone', { cron: task.schedule.cron || '', - timezone: task.schedule.timezone, + timezone: canonicalizeTimezone(task.schedule.timezone), }); } return t('sessions.scheduledTasks.dialog.schedule.cron', { cron: task.schedule.cron || '' }); diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts index f0bac895..00dd6225 100644 --- a/packages/ui/src/lib/i18n/messages/en.ts +++ b/packages/ui/src/lib/i18n/messages/en.ts @@ -296,6 +296,8 @@ export const dict = { 'sessions.scheduledTasks.editor.goal.aria': 'Run this task as a goal the agent pursues to completion', 'sessions.scheduledTasks.editor.goal.budgetLabel': 'Token budget', 'sessions.scheduledTasks.editor.goal.budgetAria': 'Limit the goal to a token budget', + 'sessions.scheduledTasks.editor.permissionAutoAccept.label': 'Auto-accept permissions', + 'sessions.scheduledTasks.editor.permissionAutoAccept.aria': 'Start the task session with permission auto-accept enabled', 'sessions.scheduledTasks.editor.actions.closeAria': 'Close', 'sessions.scheduledTasks.editor.actions.cancel': 'Cancel', 'sessions.scheduledTasks.editor.actions.save': 'Save', diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts index 799a7b08..abcade0c 100644 --- a/packages/ui/src/lib/i18n/messages/es.ts +++ b/packages/ui/src/lib/i18n/messages/es.ts @@ -297,6 +297,8 @@ export const dict: Record = { "sessions.scheduledTasks.editor.goal.aria": "Ejecutar esta tarea como un objetivo que el agente persigue hasta completarlo", "sessions.scheduledTasks.editor.goal.budgetLabel": "Presupuesto de tokens", "sessions.scheduledTasks.editor.goal.budgetAria": "Limitar el objetivo a un presupuesto de tokens", + "sessions.scheduledTasks.editor.permissionAutoAccept.label": "Aceptar permisos automáticamente", + "sessions.scheduledTasks.editor.permissionAutoAccept.aria": "Iniciar la sesión de la tarea con la aceptación automática de permisos activada", "sessions.scheduledTasks.editor.actions.closeAria": "Cerrar", "sessions.scheduledTasks.editor.actions.cancel": "Cancelar", "sessions.scheduledTasks.editor.actions.save": "Guardar", diff --git a/packages/ui/src/lib/i18n/messages/fr.ts b/packages/ui/src/lib/i18n/messages/fr.ts index a2b2f893..3dc56279 100644 --- a/packages/ui/src/lib/i18n/messages/fr.ts +++ b/packages/ui/src/lib/i18n/messages/fr.ts @@ -139,6 +139,8 @@ export const dict = { 'sessions.scheduledTasks.editor.goal.aria': 'Exécuter cette tâche comme un objectif que l\'agent poursuit jusqu\'au bout', 'sessions.scheduledTasks.editor.goal.budgetLabel': 'Budget de tokens', 'sessions.scheduledTasks.editor.goal.budgetAria': 'Limiter l’objectif à un budget de tokens', + 'sessions.scheduledTasks.editor.permissionAutoAccept.label': 'Accepter automatiquement les autorisations', + 'sessions.scheduledTasks.editor.permissionAutoAccept.aria': 'Démarrer la session de la tâche avec l’acceptation automatique des autorisations activée', 'sessions.scheduledTasks.editor.actions.closeAria': 'Fermer', 'sessions.scheduledTasks.editor.actions.cancel': 'Annuler', 'sessions.scheduledTasks.editor.actions.save': 'Sauvegarder', diff --git a/packages/ui/src/lib/i18n/messages/ja.ts b/packages/ui/src/lib/i18n/messages/ja.ts index 3243bac1..a62bda58 100644 --- a/packages/ui/src/lib/i18n/messages/ja.ts +++ b/packages/ui/src/lib/i18n/messages/ja.ts @@ -297,6 +297,8 @@ export const dict: Record = { 'sessions.scheduledTasks.editor.goal.aria': 'このタスクをエージェントが完了まで追求するゴールとして実行します', 'sessions.scheduledTasks.editor.goal.budgetLabel': 'トークン予算', 'sessions.scheduledTasks.editor.goal.budgetAria': 'ゴールをトークン予算で制限します', + 'sessions.scheduledTasks.editor.permissionAutoAccept.label': '権限を自動承認', + 'sessions.scheduledTasks.editor.permissionAutoAccept.aria': '権限の自動承認を有効にしてタスクのセッションを開始します', 'sessions.scheduledTasks.editor.actions.closeAria': '閉じる', 'sessions.scheduledTasks.editor.actions.cancel': 'キャンセル', 'sessions.scheduledTasks.editor.actions.save': '保存', diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index 8600b022..e5c2ea4d 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -297,6 +297,8 @@ export const dict: Record = { 'sessions.scheduledTasks.editor.goal.aria': '이 작업을 에이전트가 완료까지 추진하는 목표로 실행합니다', 'sessions.scheduledTasks.editor.goal.budgetLabel': '토큰 예산', 'sessions.scheduledTasks.editor.goal.budgetAria': '목표를 토큰 예산으로 제한합니다', + 'sessions.scheduledTasks.editor.permissionAutoAccept.label': '권한 자동 승인', + 'sessions.scheduledTasks.editor.permissionAutoAccept.aria': '권한 자동 승인이 켜진 상태로 작업 세션을 시작합니다', 'sessions.scheduledTasks.editor.actions.closeAria': '닫기', 'sessions.scheduledTasks.editor.actions.cancel': '취소', 'sessions.scheduledTasks.editor.actions.save': '저장', diff --git a/packages/ui/src/lib/i18n/messages/pl.ts b/packages/ui/src/lib/i18n/messages/pl.ts index e36af240..13b8f78c 100644 --- a/packages/ui/src/lib/i18n/messages/pl.ts +++ b/packages/ui/src/lib/i18n/messages/pl.ts @@ -391,6 +391,8 @@ export const dict: Record = { 'sessions.scheduledTasks.editor.goal.aria': 'Uruchom to zadanie jako cel, do którego agent dąży aż do ukończenia', 'sessions.scheduledTasks.editor.goal.budgetLabel': 'Budżet tokenów', 'sessions.scheduledTasks.editor.goal.budgetAria': 'Ogranicz cel budżetem tokenów', + 'sessions.scheduledTasks.editor.permissionAutoAccept.label': 'Automatycznie akceptuj uprawnienia', + 'sessions.scheduledTasks.editor.permissionAutoAccept.aria': 'Uruchom sesję zadania z włączonym automatycznym akceptowaniem uprawnień', 'sessions.scheduledTasks.editor.actions.closeAria': 'Zamknij', 'sessions.scheduledTasks.editor.actions.cancel': 'Anuluj', 'sessions.scheduledTasks.editor.actions.save': 'Zapisz', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index cf9d78cc..65400be9 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -297,6 +297,8 @@ export const dict: Record = { "sessions.scheduledTasks.editor.goal.aria": "Executar esta tarefa como um objetivo que o agente persegue até concluir", "sessions.scheduledTasks.editor.goal.budgetLabel": "Orçamento de tokens", "sessions.scheduledTasks.editor.goal.budgetAria": "Limitar o objetivo a um orçamento de tokens", + "sessions.scheduledTasks.editor.permissionAutoAccept.label": "Aceitar permissões automaticamente", + "sessions.scheduledTasks.editor.permissionAutoAccept.aria": "Iniciar a sessão da tarefa com a aceitação automática de permissões ativada", "sessions.scheduledTasks.editor.actions.closeAria": "Fechar", "sessions.scheduledTasks.editor.actions.cancel": "Cancelar", "sessions.scheduledTasks.editor.actions.save": "Salvar", diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index 32e452bf..22c3251d 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -297,6 +297,8 @@ export const dict: Record = { "sessions.scheduledTasks.editor.goal.aria": "Виконати це завдання як ціль, яку агент веде до завершення", "sessions.scheduledTasks.editor.goal.budgetLabel": "Бюджет токенів", "sessions.scheduledTasks.editor.goal.budgetAria": "Обмежити ціль бюджетом токенів", + "sessions.scheduledTasks.editor.permissionAutoAccept.label": "Автоматично приймати дозволи", + "sessions.scheduledTasks.editor.permissionAutoAccept.aria": "Запускати сесію завдання з увімкненим автоматичним прийняттям дозволів", "sessions.scheduledTasks.editor.actions.closeAria": "Закрити", "sessions.scheduledTasks.editor.actions.cancel": "Скасувати", "sessions.scheduledTasks.editor.actions.save": "Зберегти", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index b85d3e77..8d24a202 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -297,6 +297,8 @@ export const dict: Record = { 'sessions.scheduledTasks.editor.goal.aria': '将此任务作为代理持续推进直至完成的目标运行', 'sessions.scheduledTasks.editor.goal.budgetLabel': '令牌预算', 'sessions.scheduledTasks.editor.goal.budgetAria': '用令牌预算限制该目标', + 'sessions.scheduledTasks.editor.permissionAutoAccept.label': '自动接受权限', + 'sessions.scheduledTasks.editor.permissionAutoAccept.aria': '启动任务会话时启用权限自动接受', 'sessions.scheduledTasks.editor.actions.closeAria': '关闭', 'sessions.scheduledTasks.editor.actions.cancel': '取消', 'sessions.scheduledTasks.editor.actions.save': '保存', diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.ts b/packages/ui/src/lib/i18n/messages/zh-TW.ts index a614462e..cb38d4b4 100644 --- a/packages/ui/src/lib/i18n/messages/zh-TW.ts +++ b/packages/ui/src/lib/i18n/messages/zh-TW.ts @@ -310,6 +310,8 @@ export const dict: Record = { 'sessions.scheduledTasks.editor.goal.aria': '將此任務作為代理持續推進直至完成的目標執行', 'sessions.scheduledTasks.editor.goal.budgetLabel': '權杖預算', 'sessions.scheduledTasks.editor.goal.budgetAria': '以權杖預算限制該目標', + 'sessions.scheduledTasks.editor.permissionAutoAccept.label': '自動接受權限', + 'sessions.scheduledTasks.editor.permissionAutoAccept.aria': '啟動任務工作階段時啟用權限自動接受', 'sessions.scheduledTasks.editor.actions.closeAria': '關閉', 'sessions.scheduledTasks.editor.actions.cancel': '取消', 'sessions.scheduledTasks.editor.actions.save': '儲存', diff --git a/packages/ui/src/lib/scheduledTasksApi.ts b/packages/ui/src/lib/scheduledTasksApi.ts index 5948266c..8b215d64 100644 --- a/packages/ui/src/lib/scheduledTasksApi.ts +++ b/packages/ui/src/lib/scheduledTasksApi.ts @@ -23,6 +23,7 @@ export type ScheduledTask = { agent?: string; goalEnabled?: boolean; goalTokenBudget?: number; + permissionAutoAccept?: boolean; }; state: { createdAt: number; diff --git a/packages/ui/src/lib/timezones.ts b/packages/ui/src/lib/timezones.ts new file mode 100644 index 00000000..bbb2da7d --- /dev/null +++ b/packages/ui/src/lib/timezones.ts @@ -0,0 +1,14 @@ +// Some ICU builds still expose pre-rename IANA timezone ids. Display (and +// persist going forward) the modern names. +const LEGACY_TIMEZONE_ALIASES: Record = { + 'Europe/Kiev': 'Europe/Kyiv', + 'Asia/Calcutta': 'Asia/Kolkata', + 'Asia/Saigon': 'Asia/Ho_Chi_Minh', + 'Asia/Rangoon': 'Asia/Yangon', + 'Asia/Katmandu': 'Asia/Kathmandu', + 'America/Godthab': 'America/Nuuk', + 'Atlantic/Faeroe': 'Atlantic/Faroe', + 'Pacific/Enderbury': 'Pacific/Kanton', +}; + +export const canonicalizeTimezone = (timezone: string): string => LEGACY_TIMEZONE_ALIASES[timezone] ?? timezone; diff --git a/packages/web/server/index.js b/packages/web/server/index.js index a9fefea9..a1715a11 100644 --- a/packages/web/server/index.js +++ b/packages/web/server/index.js @@ -1068,6 +1068,7 @@ const scheduledTasksRuntime = createScheduledTasksRuntime({ buildOpenCodeUrl, getOpenCodeAuthHeaders, waitForOpenCodeReady, + setSessionAutoAccept: (sessionId, enabled, directory) => permissionAutoAcceptRuntime.setSessionPolicy(sessionId, enabled, directory), emitTaskRunEvent: (event) => { for (const client of uiOpenChamberEventClients) { try { diff --git a/packages/web/server/lib/projects/project-config.js b/packages/web/server/lib/projects/project-config.js index 67c5f284..ed8d98b6 100644 --- a/packages/web/server/lib/projects/project-config.js +++ b/packages/web/server/lib/projects/project-config.js @@ -214,6 +214,7 @@ const normalizeExecution = (value) => { const variant = asNonEmptyString(value.variant); const agent = asNonEmptyString(value.agent); const goalEnabled = value.goalEnabled === true; + const permissionAutoAccept = value.permissionAutoAccept === true; const goalTokenBudget = typeof value.goalTokenBudget === 'number' && Number.isFinite(value.goalTokenBudget) && value.goalTokenBudget > 0 @@ -238,6 +239,7 @@ const normalizeExecution = (value) => { ...(agent ? { agent } : {}), ...(goalEnabled ? { goalEnabled: true } : {}), ...(goalEnabled && goalTokenBudget ? { goalTokenBudget } : {}), + ...(permissionAutoAccept ? { permissionAutoAccept: true } : {}), }; }; diff --git a/packages/web/server/lib/scheduled-tasks/runtime.js b/packages/web/server/lib/scheduled-tasks/runtime.js index 7534cf97..c1f42fd6 100644 --- a/packages/web/server/lib/scheduled-tasks/runtime.js +++ b/packages/web/server/lib/scheduled-tasks/runtime.js @@ -225,6 +225,7 @@ export const createScheduledTasksRuntime = (deps) => { getOpenCodeAuthHeaders, waitForOpenCodeReady, emitTaskRunEvent, + setSessionAutoAccept, logger = console, maxGlobalConcurrency = DEFAULT_GLOBAL_CONCURRENCY, maxProjectConcurrency = DEFAULT_PROJECT_CONCURRENCY, @@ -611,6 +612,17 @@ export const createScheduledTasksRuntime = (deps) => { } catch { } + if (task.execution.permissionAutoAccept && typeof setSessionAutoAccept === 'function') { + // Enroll before the prompt goes out so the very first permission request + // is already auto-approved. Enrollment failure must not kill the run — + // the task still executes, permissions just wait for the user. + try { + await setSessionAutoAccept(sessionID, true, projectPath); + } catch (error) { + logger.warn?.('[scheduled-tasks] failed to enable permission auto-accept for session', sessionID, error?.message ?? error); + } + } + if (task.execution.goalEnabled) { await createTaskGoal({ baseUrl, authHeaders, sessionID, projectPath, task }); }