1import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
2import { threadSupportsProviderHandoff } from "@t3tools/client-runtime/state/thread-workflows";
3import type { OrchestrationV2ThreadProjection } from "@t3tools/contracts";
4
5type ThreadStartMarkers = Pick<
6 EnvironmentThreadShell,
7 "latestRun" | "latestUserMessageAt" | "runtime"
8>;
9
10function threadShellHasStarted(thread: ThreadStartMarkers | null | undefined): boolean {
11 return Boolean(
12 thread &&
13 (thread.latestRun !== null || thread.latestUserMessageAt !== null || thread.runtime !== null),
14 );
15}
16
17/**
18 * Whether this thread's model picker may offer providers other than the one it
19 * runs on, matching web's locked-provider rule: a thread that never ran a turn
20 * is bound to nothing, and a started thread stays on its provider only when its
21 * session cannot hand the conversation to another one.
22 */
23export function threadAllowsProviderSwitch(input: {
24 readonly thread: ThreadStartMarkers | null | undefined;
25 readonly projection: OrchestrationV2ThreadProjection | null | undefined;
26}): boolean {
27 return input.projection != null
28 ? threadSupportsProviderHandoff(input.projection)
29 : !threadShellHasStarted(input.thread);
30}