Skip to content

feat(desktop): per-job reasoning effort picker in the cron editor - #93004

Open
the3asic wants to merge 1 commit into
NousResearch:mainfrom
the3asic:desktop-cron-reasoning-effort
Open

feat(desktop): per-job reasoning effort picker in the cron editor#93004
the3asic wants to merge 1 commit into
NousResearch:mainfrom
the3asic:desktop-cron-reasoning-effort

Conversation

@the3asic

Copy link
Copy Markdown
Contributor

Summary

Surfaces the per-job reasoning_effort pin (landed in 4e1dd1a, shipped v0.20.5) in the desktop cron UI. The backend commit covered CLI + agent tool + docs; #92101 is in flight for the web dashboard; this covers the Electron/desktop surface, ported from the desktop section of #65679 (closed in favor of the landed backend).

What it adds

  • Editor dropdown next to the per-job model picker, same optional-override pattern: inherit (default — follow config resolution at fire time: agent.reasoning_overrides > agent.reasoning_effort), none (explicitly disabled — the shared parser maps none/false/disabled/YAML false to {"enabled": False}), minimalultra.
  • Detail panel row shows the pinned effort only when set, localized.
  • Save contract mirrors the model/provider axes: agent jobs always write the field so resetting to inherit clears a previous pin (null); script-only (no_agent) jobs never render the picker and never touch the stored field (the scheduler ignores it for them).
  • Types: CronJob / CronJobCreatePayload / CronJobUpdates carry reasoning_effort?: boolean | null | string.
  • i18n: en / ja / zh / zh-hant.

Validation

  • vitest run src/app/cron/cron-job-model.test.ts — 16 passed (incl. new set/clear/script-only-untouched assertions)
  • npm run typecheck — renderer + electron + e2e tsconfigs clean

Surfaces the per-job reasoning_effort pin (4e1dd1a, shipped in v0.20.5)
in the desktop cron UI:

- editor dropdown with inherit (default: follow config resolution at
  fire time) / none (explicitly disabled) / minimal..ultra, following the
  same optional-override pattern as the per-job model picker
- detail panel shows the pinned effort only when set, with localized
  labels
- script-only (no_agent) jobs never render the picker and never touch
  the stored field — the scheduler ignores it for them
- saving always writes the axis for agent jobs so resetting to inherit
  clears a previous pin (null), matching the model/provider override
  contract in cronEditorUpdates
- CronJob / CreatePayload / Updates types carry the field
  (boolean | null | string — YAML false means disabled, mirroring
  hermes_constants.parse_reasoning_effort)
- i18n: en / ja / zh / zh-hant labels

Ported from NousResearch#65679 (closed in favor of the landed backend); desktop was
the surface that implementation covered and the landed commit did not.
The web dashboard surface is separately in flight in NousResearch#92101.
@alt-glitch alt-glitch added type/feature New feature or request comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have labels Aug 23, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

Complete feature slice: model layer, editor UI, detail view, payload typing, all four locales plus the shared type, and unit tests covering set/clear and the script-only exemption. The false → 'none' mapping for YAML booleans is a thoughtful touch.

Findings:

  1. Duplicated wire-normalization — apps/desktop/src/app/cron/cron-job-model.ts:95-98 and apps/desktop/src/app/cron/index.tsx:596 implement the identical inherit|''→null ternary in two places. One will eventually drift (e.g. someone adds a new sentinel in one copy). Extract a tiny shared helper (e.g. reasoningEffortToWire(values.reasoningEffort)) next to cronEditorUpdates and call it from both sites.

  2. Unvalidated stored values reach the picker — apps/desktop/src/app/cron/index.tsx:1133 seeds the Select with jobReasoningEffort(job) || 'inherit'. A legacy/garbage stored string (say 'enabled') isn't in REASONING_VALUES, so the trigger renders blank and saving re-persists the junk unchanged. Clamp on load: if the value isn't in REASONING_VALUES, fall back to 'inherit'.

  3. Boolean true case — the type at apps/desktop/src/types/hermes.ts:855 allows boolean, and jobReasoningEffort (index.tsx:168-175) special-cases false but lets true fall into asText(...) → literal "true" shown via the ?? value fallback in both the detail row and picker. If the backend can ever store true ("enabled, default level"), map it deliberately; otherwise narrowing the type to null | string with false kept only where YAML round-trips would document intent better than boolean.

  4. REASONING_VALUES parity — the list (index.tsx:116-126) mirrors cron/jobs.py::_normalize_reasoning_effort by convention only. Since the backend is the validator, consider generating or asserting this list against the backend constant in a sync test so a new server-side level doesn't silently become unpickable.

Minor: i18n/types.ts:1707 types reasoningLabels as Record<string, string>; keying it to the REASONING_VALUES union would catch missing translations at compile time instead of showing raw slugs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants