import type { ModelSelection, OrchestrationV2ProviderCapabilities, ProviderDriverKind, ProviderInteractionMode, RuntimeMode,} from "@t3tools/contracts";import { modelSelectionsEqual } from "@t3tools/shared/model"; import type { ProviderContinuationIdentity } from "../provider/ProviderDriver.ts";import type { ProviderSelectionTransitionPlan } from "./ProviderSelectionTransition.ts"; export type ProviderSessionTransition = | { readonly type: "reuse" } | { readonly type: "switch_model_in_session" } | { readonly type: "restart_and_resume" } | { readonly type: "create_with_handoff" } | { readonly type: "reject"; readonly reason: string }; export interface ProviderSessionTransitionState { readonly driver: ProviderDriverKind; readonly continuationIdentity: ProviderContinuationIdentity; readonly modelSelection: ModelSelection; readonly runtimeMode: RuntimeMode; readonly interactionMode: ProviderInteractionMode; readonly workspace: string; readonly capabilities: OrchestrationV2ProviderCapabilities;} export interface ProviderSessionTransitionTarget extends ProviderSessionTransitionState { readonly available: boolean;} export function decideProviderSessionTransition(input: { readonly current: ProviderSessionTransitionState | null; readonly target: ProviderSessionTransitionTarget; readonly selectionTransition?: ProviderSelectionTransitionPlan;}): ProviderSessionTransition { if (!input.target.available) { return { type: "reject", reason: "The target provider instance is unavailable." }; } if (input.current === null) { return { type: "create_with_handoff" }; } const current = input.current; const target = input.target; const continuationCompatible = current.continuationIdentity.driverKind === target.continuationIdentity.driverKind && current.continuationIdentity.continuationKey === target.continuationIdentity.continuationKey; if (!continuationCompatible || current.driver !== target.driver) { return { type: "create_with_handoff" }; } const instanceChanged = current.modelSelection.instanceId !== target.modelSelection.instanceId; const runtimeChanged = current.runtimeMode !== target.runtimeMode; const workspaceChanged = current.workspace !== target.workspace; const selectionChanged = !modelSelectionsEqual(current.modelSelection, target.modelSelection); if (selectionChanged && !instanceChanged) { switch (input.selectionTransition?.type) { case "create_with_handoff": return { type: "create_with_handoff" }; case "reject": return { type: "reject", reason: input.selectionTransition.reason }; case undefined: return { type: "reject", reason: "The provider adapter did not classify the selection change.", }; case "apply_on_next_turn": case "restart_session": break; } } if (instanceChanged || runtimeChanged || workspaceChanged) { return { type: "restart_and_resume" }; } if (selectionChanged) { switch (input.selectionTransition?.type) { case "apply_on_next_turn": return { type: "switch_model_in_session" }; case "restart_session": return { type: "restart_and_resume" }; case "create_with_handoff": return { type: "create_with_handoff" }; case "reject": return { type: "reject", reason: input.selectionTransition.reason }; case undefined: return { type: "restart_and_resume" }; } } // Interaction mode is turn-scoped and is applied when the next turn starts. return { type: "reuse" };}
import type {
ModelSelection,
OrchestrationV2ProviderCapabilities,
ProviderDriverKind,
ProviderInteractionMode,
RuntimeMode,
} from "@t3tools/contracts";
import { modelSelectionsEqual } from "@t3tools/shared/model";
import type { ProviderContinuationIdentity } from "../provider/ProviderDriver.ts";
import type { ProviderSelectionTransitionPlan } from "./ProviderSelectionTransition.ts";
export type ProviderSessionTransition =
| { readonly type: "reuse" }
| { readonly type: "switch_model_in_session" }
| { readonly type: "restart_and_resume" }
| { readonly type: "create_with_handoff" }
| { readonly type: "reject"; readonly reason: string };
export interface ProviderSessionTransitionState {
readonly driver: ProviderDriverKind;
readonly continuationIdentity: ProviderContinuationIdentity;
readonly modelSelection: ModelSelection;
readonly runtimeMode: RuntimeMode;
readonly interactionMode: ProviderInteractionMode;
readonly workspace: string;
readonly capabilities: OrchestrationV2ProviderCapabilities;
}
export interface ProviderSessionTransitionTarget extends ProviderSessionTransitionState {
readonly available: boolean;
}
export function decideProviderSessionTransition(input: {
readonly current: ProviderSessionTransitionState | null;
readonly target: ProviderSessionTransitionTarget;
readonly selectionTransition?: ProviderSelectionTransitionPlan;
}): ProviderSessionTransition {
if (!input.target.available) {
return { type: "reject", reason: "The target provider instance is unavailable." };
}
if (input.current === null) {
return { type: "create_with_handoff" };
}
const current = input.current;
const target = input.target;
const continuationCompatible =
current.continuationIdentity.driverKind === target.continuationIdentity.driverKind &&
current.continuationIdentity.continuationKey === target.continuationIdentity.continuationKey;
if (!continuationCompatible || current.driver !== target.driver) {
return { type: "create_with_handoff" };
}
const instanceChanged = current.modelSelection.instanceId !== target.modelSelection.instanceId;
const runtimeChanged = current.runtimeMode !== target.runtimeMode;
const workspaceChanged = current.workspace !== target.workspace;
const selectionChanged = !modelSelectionsEqual(current.modelSelection, target.modelSelection);
if (selectionChanged && !instanceChanged) {
switch (input.selectionTransition?.type) {
case "create_with_handoff":
return { type: "create_with_handoff" };
case "reject":
return { type: "reject", reason: input.selectionTransition.reason };
case undefined:
return {
type: "reject",
reason: "The provider adapter did not classify the selection change.",
};
case "apply_on_next_turn":
case "restart_session":
break;
}
}
if (instanceChanged || runtimeChanged || workspaceChanged) {
return { type: "restart_and_resume" };
}
if (selectionChanged) {
switch (input.selectionTransition?.type) {
case "apply_on_next_turn":
return { type: "switch_model_in_session" };
case "restart_session":
return { type: "restart_and_resume" };
case "create_with_handoff":
return { type: "create_with_handoff" };
case "reject":
return { type: "reject", reason: input.selectionTransition.reason };
case undefined:
return { type: "restart_and_resume" };
}
}
// Interaction mode is turn-scoped and is applied when the next turn starts.
return { type: "reuse" };
}