diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 0099f9df7776..ead57b5cbf2b 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@t3tools/desktop", - "version": "0.0.23", + "version": "0.0.24", "private": true, "type": "module", "main": "dist-electron/main.cjs", diff --git a/apps/server/package.json b/apps/server/package.json index 8d813de54563..387c880a9ab4 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "t3", - "version": "0.0.23", + "version": "0.0.24", "license": "MIT", "repository": { "type": "git", diff --git a/apps/web/package.json b/apps/web/package.json index 419721fde73e..03f77c3aba8f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@t3tools/web", - "version": "0.0.23", + "version": "0.0.24", "private": true, "type": "module", "scripts": { diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 96fc34c10138..b19e12403642 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -727,9 +727,16 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) model: selectedModel, models: selectedProviderModels, prompt, - modelOptions: composerModelOptions?.[selectedProvider], + modelOptions: composerModelOptions?.[selectedInstanceId], }), - [composerModelOptions, prompt, selectedModel, selectedProvider, selectedProviderModels], + [ + composerModelOptions, + prompt, + selectedInstanceId, + selectedModel, + selectedProvider, + selectedProviderModels, + ], ); const selectedPromptEffort = composerProviderState.promptEffort; @@ -1018,21 +1025,23 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) const providerTraitsMenuContent = renderProviderTraitsMenuContent({ provider: selectedProvider, + instanceId: selectedInstanceId, ...(routeKind === "server" ? { threadRef: routeThreadRef } : {}), ...(routeKind === "draft" && draftId ? { draftId } : {}), model: selectedModel, models: selectedProviderModels, - modelOptions: composerModelOptions?.[selectedProvider], + modelOptions: composerModelOptions?.[selectedInstanceId], prompt, onPromptChange: setPromptFromTraits, }); const providerTraitsPicker = renderProviderTraitsPicker({ provider: selectedProvider, + instanceId: selectedInstanceId, ...(routeKind === "server" ? { threadRef: routeThreadRef } : {}), ...(routeKind === "draft" && draftId ? { draftId } : {}), model: selectedModel, models: selectedProviderModels, - modelOptions: composerModelOptions?.[selectedProvider], + modelOptions: composerModelOptions?.[selectedInstanceId], prompt, onPromptChange: setPromptFromTraits, }); diff --git a/apps/web/src/components/chat/TraitsPicker.tsx b/apps/web/src/components/chat/TraitsPicker.tsx index 62caa323fd3b..76ff055a5941 100644 --- a/apps/web/src/components/chat/TraitsPicker.tsx +++ b/apps/web/src/components/chat/TraitsPicker.tsx @@ -1,5 +1,6 @@ import { type ProviderDriverKind, + type ProviderInstanceId, type ProviderOptionDescriptor, type ProviderOptionSelection, type ScopedThreadRef, @@ -196,6 +197,7 @@ export function shouldRenderTraitsControls(input: { export interface TraitsMenuContentProps { provider: ProviderDriverKind; + instanceId?: ProviderInstanceId; models: ReadonlyArray; model: string | null | undefined; prompt: string; @@ -208,6 +210,7 @@ export interface TraitsMenuContentProps { export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ provider, + instanceId, models, model, prompt, @@ -228,11 +231,12 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ return; } setProviderModelOptions(threadTarget, provider, nextOptions, { + ...(instanceId ? { instanceId } : {}), model, persistSticky: true, }); }, - [model, persistence, provider, setProviderModelOptions], + [instanceId, model, persistence, provider, setProviderModelOptions], ); const { descriptors, @@ -343,6 +347,7 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ export const TraitsPicker = memo(function TraitsPicker({ provider, + instanceId, models, model, prompt, @@ -431,6 +436,7 @@ export const TraitsPicker = memo(function TraitsPicker({ { ); }); + it("stores provider option changes on a selected custom instance", () => { + const store = useComposerDraftStore.getState(); + + store.setProviderModelOptions( + threadRef, + CODEX_DRIVER, + toSelections({ reasoningEffort: "low" }), + { + instanceId: CODEX_SECONDARY_INSTANCE, + model: "gpt-5-codex", + persistSticky: true, + }, + ); + + expect( + draftFor(threadId, TEST_ENVIRONMENT_ID)?.modelSelectionByProvider[CODEX_SECONDARY_INSTANCE], + ).toEqual( + expect.objectContaining({ + instanceId: CODEX_SECONDARY_INSTANCE, + options: [{ id: "reasoningEffort", value: "low" }], + }), + ); + expect(draftFor(threadId, TEST_ENVIRONMENT_ID)?.activeProvider).toBe(CODEX_SECONDARY_INSTANCE); + expect(useComposerDraftStore.getState().stickyActiveProvider).toBe(CODEX_SECONDARY_INSTANCE); + expect(useComposerDraftStore.getState().stickyModelSelectionByProvider[CODEX_INSTANCE]).toBe( + undefined, + ); + expect( + useComposerDraftStore.getState().stickyModelSelectionByProvider[CODEX_SECONDARY_INSTANCE], + ).toEqual( + expect.objectContaining({ + instanceId: CODEX_SECONDARY_INSTANCE, + options: [{ id: "reasoningEffort", value: "low" }], + }), + ); + }); + it("updates only the draft when sticky persistence is disabled", () => { const store = useComposerDraftStore.getState(); diff --git a/apps/web/src/composerDraftStore.ts b/apps/web/src/composerDraftStore.ts index d554ad7b0d32..5c90197a406d 100644 --- a/apps/web/src/composerDraftStore.ts +++ b/apps/web/src/composerDraftStore.ts @@ -371,6 +371,7 @@ interface ComposerDraftStoreState { provider: ProviderDriverKind, nextProviderOptions: ReadonlyArray | null | undefined, options?: { + instanceId?: ProviderInstanceId | null | undefined; model?: string | null | undefined; persistSticky?: boolean; }, @@ -2456,7 +2457,7 @@ const composerDraftStore = create()( if (normalizedProvider === null) { return; } - const instanceKey = defaultInstanceIdForDriver(normalizedProvider); + const instanceKey = options?.instanceId ?? defaultInstanceIdForDriver(normalizedProvider); const fallbackModel = normalizeModelSlug(options?.model, normalizedProvider) ?? DEFAULT_MODEL_BY_PROVIDER[normalizedProvider] ?? @@ -2501,7 +2502,9 @@ const composerDraftStore = create()( const { options: _, ...rest } = stickyBase; nextStickyMap[instanceKey] = rest as ModelSelection; } - nextStickyActiveProvider = base.activeProvider ?? instanceKey; + nextStickyActiveProvider = options.instanceId + ? instanceKey + : (base.activeProvider ?? instanceKey); } if ( @@ -2514,6 +2517,7 @@ const composerDraftStore = create()( const nextDraft: ComposerThreadDraftState = { ...base, + ...(options?.instanceId ? { activeProvider: instanceKey } : {}), modelSelectionByProvider: nextMap, }; const nextDraftsByThreadKey = { ...state.draftsByThreadKey }; diff --git a/docs/upstream/patches/0002-workspace-shell-reframe.md b/docs/upstream/patches/0002-workspace-shell-reframe.md index e90b19909f3c..53414a2c1dfd 100644 --- a/docs/upstream/patches/0002-workspace-shell-reframe.md +++ b/docs/upstream/patches/0002-workspace-shell-reframe.md @@ -62,6 +62,7 @@ composition, which are likely to continue evolving upstream. - `working tree`: initial workspace shell, AI pane framing, and hosted-shell onboarding updates - `21ed1770`: explicit AI pane extraction through a reusable workspace pane frame and shared chat action bar +- `ccd33a35`: upstream sync merge against `upstream/main` through `4f0f24f0` ## Sync Notes @@ -83,3 +84,6 @@ composition, which are likely to continue evolving upstream. - split future implementation into smaller patch files when the shell reframe turns into concrete workstreams such as sidebar mapping, pane extraction, or layout persistence +- Verified during the 2026-05-26 upstream sync against upstream commit + `4f0f24f0`; upstream composer reasoning-selection updates touched shared chat + state and UI but did not require local workspace-shell code changes. diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 8d3ed21884a0..5b2ad2cc412d 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@t3tools/contracts", - "version": "0.0.23", + "version": "0.0.24", "private": true, "files": [ "dist"