Skip to content

Let a dedicated model name sessions - #776

Open
uvforce wants to merge 2 commits into
agegr:mainfrom
uvforce:pr/session-title-model
Open

Let a dedicated model name sessions#776
uvforce wants to merge 2 commits into
agegr:mainfrom
uvforce:pr/session-title-model

Conversation

@uvforce

@uvforce uvforce commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Problem

Session titles are generated with the session's own model. Naming a conversation therefore spends a request on whatever large model happens to be selected, and buildSessionTitleAgentOptions copies that session's thinkingLevel, so a one-line title can be produced with a full reasoning budget.

Change

  • General settings → Session title selects the model used for naming. The default, Use the session's own model, keeps today's behaviour.
  • The choice is stored as sessionTitleModel in settings.json, using the same locked read/write pattern as the existing PowerShell tool preference, and served by /api/session-title/settings following the shape of /api/tools/settings.
  • generateSessionTitle() accepts a provider/model-id override and resolves it through the session's own modelRuntime, so only configured models can be used.
  • Thinking is reduced to the cheapest level each model accepts. Gemini gets low because it rejects minimal and its SDK reports that level whenever thinking is disabled.

Tool shadowing and message sanitization are untouched, so naming still cannot execute tools or mutate the source session.

Tests

  • lib/session-title-settings.test.mjs — defaults, persistence, removal of the key when inheriting, 0600 permissions.
  • app/api/session-title/settings/route.test.mjs — GET/PUT round trip and payload validation.
  • lib/session-title.test.mjs — model override and thinking level resolution.
  • Full npm test (956), tsc --noEmit, eslint, and next build pass.
  • Verified in a production build with a real browser: the section renders in General, the stored model is shown when the catalogue is unavailable, and switching back to the default removes the key from settings.json.

Notes

The model catalogue is project-scoped, so the dropdown lists models only when a project is open; the stored value is still displayed without one.

Session titles are generated by the session's own model, so naming a
conversation costs a request on whatever large model is in use, and it
inherits that session's thinking level even though a title needs no
reasoning budget.

Add a Session title section to General settings that selects any
configured model for naming, stored as sessionTitleModel in settings.json
next to the existing preferences. The default keeps today's behaviour by
inheriting the session model. Thinking is reduced to the cheapest level
each model accepts, with "low" for Gemini because it rejects "minimal"
and its SDK reports that level when thinking is disabled.

The title agent still runs with shadowed tools and sanitized messages, so
naming cannot execute tools or mutate the source session.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T09:10:17.112415Z 7a931da PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7a931da4cc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/session-title.ts Outdated
Comment on lines +265 to +268
modelOverride = source.modelRuntime?.getModel(
options.model.slice(0, slash),
options.model.slice(slash + 1),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject an unavailable dedicated title model

When the stored model is absent from this session's modelRuntime—for example, after adding a model while an existing wrapper is alive, or when a globally selected custom model is unavailable in another project—getModel() returns undefined and buildSessionTitleAgentOptions() silently falls back to the session model. The naming request can therefore use the expensive model the user explicitly replaced; refresh the runtime as the set_model path does and report an error if the configured model still cannot be resolved.

Useful? React with 👍 / 👎.

Comment thread lib/session-title.ts Outdated
Comment on lines +59 to +63
const supported = getSupportedThinkingLevels(model);
if (supported.includes("off")) return "off";
if (supported.includes("low")) return "low";
if (supported.includes("minimal")) return "minimal";
return "off";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Select the actual lowest supported thinking level

For a reasoning model whose supported levels exclude off, this chooses low before the cheaper minimal; if the model exposes only medium or higher through a custom thinkingLevelMap, it returns unsupported off. Those configurations either spend more than advertised or can fail title generation, so choose the first genuinely supported level in canonical cost order and handle an empty supported set explicitly.

Useful? React with 👍 / 👎.

Comment on lines +118 to +119
return () => { cancelled = true; };
}, [cwd]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh title-model options after model configuration changes

If General has already mounted, navigating to Models, adding a model, saving it, and returning to General leaves this effect dormant because cwd did not change and mounted settings sections are only hidden rather than unmounted. The newly configured model therefore cannot be selected until the entire settings dialog is closed and reopened; reload the catalogue when General is activated or after model configuration is saved.

Useful? React with 👍 / 👎.

…t level

Three points from review:

- A configured model that the session's runtime cannot resolve no longer
  falls back to the session model, which would silently spend the request
  the dedicated model exists to avoid. The runtime is refreshed first, as
  set_model does, so a model added while this runtime was already alive is
  still found; if it is still missing, naming fails with a clear error.

- The thinking level is the first entry of getSupportedThinkingLevels(),
  which is already in ascending cost order, instead of a fixed off/low/
  minimal preference. A model that excludes "off" now gets "minimal", and a
  model that only exposes "medium" and above gets "medium" rather than an
  unsupported "off". Gemini still skips "minimal".

- Settings sections stay mounted once visited, so the model catalogue is
  reloaded each time General becomes the active section. A model added
  under Models is selectable without closing the dialog.
@uvforce

uvforce commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all three review points in cefd1e4:

  • A configured model that the runtime cannot resolve now fails with Session title model not found instead of silently naming with the session model. The runtime is refreshed first (refresh({ allowNetwork: false }), as set_model does), so a model added while this session's runtime was already alive is still found.
  • resolveTitleThinkingLevel takes the first entry of getSupportedThinkingLevels(), which is already in ascending cost order. A model excluding off gets minimal; one exposing only medium and above gets medium. Gemini still skips minimal.
  • The model catalogue reloads whenever General becomes the active section, so a model added under Models is selectable without closing the dialog.

Tests cover the refresh-then-reject path, both level cases, and the reload wiring.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant