966 lines · 32.6 KB · TypeScript
RawDownload
import {  ANTIGRAVITY_DEFAULT_MODEL,  type AssetCreateUrlInput,  type AssetCreateUrlResult,  type ChatFileAttachment,  type EnvironmentId,  isProviderDriverKind,  ProjectId,  type MessageId,  type ModelSelection,  type OrchestrationV2ProjectedTurnItem,  type ProviderInteractionMode,  ProviderDriverKind,  type ProviderInstanceId,  type ServerProvider,  type ScopedProjectRef,  type ScopedThreadRef,  type ThreadId,  type RunId,} from "@t3tools/contracts";import * as DateTime from "effect/DateTime";import { resolveAssetUrl } from "@t3tools/client-runtime/state/assets";import {  squashAtomCommandFailure,  type AtomCommandResult,} from "@t3tools/client-runtime/state/runtime";import { videoMimeType } from "@t3tools/shared/video";import {  appendCodexArtifactTemplateUsePrompt,  codexArtifactTemplateUsePrompt,  type CodexArtifactTemplate,} from "@t3tools/client-runtime/codex-artifact-templates";import { presentThreadShell } from "@t3tools/client-runtime/state/shell";import {  type ChatMessage,  isImageAttachment,  type SessionPhase,  type Thread,  type TurnDiffSummary,} from "../types";import { type ComposerImageAttachment, type DraftThreadState } from "../composerDraftStore";import * as Schema from "effect/Schema";import { appAtomRegistry } from "../rpc/atomRegistry";import { environmentThreadShells } from "../state/threads";import { waitForAtomValue } from "../state/waitForAtomValue";import {  filterTerminalContextsWithText,  stripInlineTerminalContextPlaceholders,  type TerminalContextDraft,} from "../lib/terminalContext";import type { DraftThreadEnvMode } from "../composerDraftStore";import type { ComposerSubmissionIntent } from "../composer-logic";import type { TimelineEntry } from "../session-logic";import type { DesktopPreviewOverlay } from "../previewStateStore";import type { RightPanelSurface } from "../rightPanelStore";import {  NO_PROVIDER_MODEL_SELECTION,  resolveSelectableProviderInstanceEntry,  type ProviderInstanceEntry,} from "../providerInstances"; export const LAST_INVOKED_SCRIPT_BY_PROJECT_KEY = "t3code:last-invoked-script-by-project";export const MAX_HIDDEN_MOUNTED_TERMINAL_THREADS = 10;export const MAX_HIDDEN_MOUNTED_PREVIEW_THREADS = 3;export const ENVIRONMENT_RECONNECT_WARNING_GRACE_MS = 2_000; export function agentControlledBrowserCloseConfirmation(  surfaces: readonly RightPanelSurface[],  desktopByTabId: Readonly<Record<string, Pick<DesktopPreviewOverlay, "controller"> | undefined>>,): string | null {  const activeBrowserCount = surfaces.filter(    (surface) =>      surface.kind === "preview" &&      surface.resourceId !== null &&      desktopByTabId[surface.resourceId]?.controller === "agent",  ).length;  if (activeBrowserCount === 0) return null;  if (activeBrowserCount === 1) {    return [      "Close browser while the agent is using it?",      "The agent is actively controlling this browser. Closing it may interrupt the current browser action.",    ].join("\n");  }  return [    `Close ${activeBrowserCount} browsers while the agent is using them?`,    "The agent is actively controlling these browsers. Closing them may interrupt the current browser actions.",  ].join("\n");} export function shouldRenderPreviewMiniPlayer(  miniPlayerTabId: string | null,  renderedRightPanelSurface: RightPanelSurface | null,): boolean {  return (    miniPlayerTabId !== null &&    !(      renderedRightPanelSurface?.kind === "preview" &&      renderedRightPanelSurface.resourceId === miniPlayerTabId    )  );} export function shouldOpenProactivePullRequest(  previousTargetKey: string | null | undefined,  targetKey: string | null,): boolean {  return previousTargetKey !== undefined && targetKey !== null && targetKey !== previousTargetKey;} export function shouldOpenProactiveTurnDiff(input: {  previousRunningTurnId: RunId | null | undefined;  runningTurnId: RunId | null;  settledTurnId: RunId | null;  turnCompleted: boolean;}): boolean {  return (    input.previousRunningTurnId !== undefined &&    input.previousRunningTurnId !== null &&    input.runningTurnId === null &&    input.turnCompleted &&    input.settledTurnId === input.previousRunningTurnId  );} export function resolveProactiveTurnDiffAction(input: {  checkpoint: Pick<TurnDiffSummary, "status" | "files"> | undefined;  isGitRepo: boolean | undefined;  activeSurfaceKind: RightPanelSurface["kind"] | null;}): "defer" | "ignore" | "open" {  if (input.activeSurfaceKind === "pull-request") return "ignore";  if (input.checkpoint === undefined || input.checkpoint.status === "missing") return "defer";  if (input.isGitRepo === undefined) return "defer";  if (    !input.isGitRepo ||    input.checkpoint.status !== "ready" ||    input.checkpoint.files.length === 0  ) {    return "ignore";  }  return "open";} export function codexArtifactTemplatePromptToAppend(  currentDraft: string,  template: CodexArtifactTemplate,): string | null {  return appendCodexArtifactTemplateUsePrompt(currentDraft, template) === currentDraft    ? null    : codexArtifactTemplateUsePrompt(template);} export const LastInvokedScriptByProjectSchema = Schema.Record(ProjectId, Schema.String); export function resolveEffectiveInteractionMode(input: {  planModeEnabled: boolean;  composerInteractionMode: ProviderInteractionMode | null;  threadInteractionMode: ProviderInteractionMode | null | undefined;}): ProviderInteractionMode {  if (!input.planModeEnabled) return "default";  return input.composerInteractionMode ?? input.threadInteractionMode ?? "default";} export function shouldDockDraftHeroForSubmission(input: {  isDraftHeroState: boolean;  activeThreadKey: string | null;  submissionIntent: ComposerSubmissionIntent;}): boolean {  return (    input.submissionIntent === "foreground" &&    input.isDraftHeroState &&    input.activeThreadKey !== null  );} export function shouldReleaseTimelineAnchorForToolActivity(input: {  anchorMessageId: MessageId | null;  liveFollowEnabled: boolean;  runningTurnId: RunId | null;  timelineEntries: ReadonlyArray<TimelineEntry>;}): boolean {  if (input.anchorMessageId === null || !input.liveFollowEnabled || input.runningTurnId === null) {    return false;  }   return input.timelineEntries.some((timelineEntry) => {    if (timelineEntry.kind !== "work" || timelineEntry.entry.runId !== input.runningTurnId) {      return false;    }     const entry = timelineEntry.entry;    return (      entry.tone === "tool" ||      entry.itemType !== undefined ||      entry.requestKind !== undefined ||      (entry.command?.trim().length ?? 0) > 0    );  });} export function toolGroupConsumesUpwardNavigation(target: EventTarget | null): boolean {  const elementTarget = target instanceof Element ? target : null;  const group = elementTarget?.closest<HTMLElement>("[data-tool-group-scroll]");  if (!group) return false;   // A nested result or the group itself can consume an upward scroll.  for (let element = elementTarget; element; element = element.parentElement) {    if (element.scrollTop > 0) {      const overflowY = getComputedStyle(element).overflowY;      if (overflowY === "auto" || overflowY === "scroll") return true;    }    if (element === group) break;  }  return false;} export function resolveDraftHeroState(input: {  isLocalDraftThread: boolean;  hasTimelineEntries: boolean;  isWorking: boolean;  draftHeroDockRequested: boolean;  backgroundSubmissionPending: boolean;}): boolean {  if (input.backgroundSubmissionPending) {    return true;  }  return (    input.isLocalDraftThread &&    !input.hasTimelineEntries &&    !input.isWorking &&    !input.draftHeroDockRequested  );} export function resolveDraftPromotionNavigationTarget(input: {  serverThreadRef: ScopedThreadRef | null;  serverThread: Pick<Thread, "latestRun"> | null | undefined;  backgroundSubmissionPending: boolean;}): ScopedThreadRef | null {  if (input.backgroundSubmissionPending) {    return null;  }  const latestRun = input.serverThread?.latestRun ?? null;  const runStarted = latestRun?.startedAt != null;  const startupStopped =    latestRun?.status === "failed" ||    latestRun?.status === "interrupted" ||    latestRun?.status === "cancelled";  // Keep local preparation feedback mounted until the server can render the  // running turn or its startup error on the canonical thread route.  return runStarted || startupStopped ? input.serverThreadRef : null;} export function scheduleEnvironmentReconnectWarning(showWarning: () => void): () => void {  const timeoutId = globalThis.setTimeout(showWarning, ENVIRONMENT_RECONNECT_WARNING_GRACE_MS);  return () => globalThis.clearTimeout(timeoutId);} export function hasEnvironmentReconnectWarningGraceElapsed(  activeEnvironmentId: EnvironmentId | null,  elapsedEnvironmentId: EnvironmentId | null,): boolean {  return activeEnvironmentId !== null && activeEnvironmentId === elapsedEnvironmentId;} export function startNewThreadForProject(  projectRef: ScopedProjectRef | null,  handleNewThread: (projectRef: ScopedProjectRef) => Promise<unknown>,): boolean {  if (projectRef === null) return false;  void handleNewThread(projectRef);   return true;} export function resolveThreadMetadataUpdateForNextTurn(input: {  currentModelSelection: ModelSelection;  nextModelSelection?: ModelSelection;  currentBranch: string | null;  nextBranch?: string;}): {  modelSelection?: ModelSelection;  branch?: string;  worktreePath?: null;} | null {  const nextModelSelection = input.nextModelSelection;  const modelSelectionChanged =    nextModelSelection !== undefined &&    (nextModelSelection.model !== input.currentModelSelection.model ||      nextModelSelection.instanceId !== input.currentModelSelection.instanceId ||      JSON.stringify(nextModelSelection.options ?? null) !==        JSON.stringify(input.currentModelSelection.options ?? null));  const branchChanged = input.nextBranch !== undefined && input.nextBranch !== input.currentBranch;  if (!modelSelectionChanged && !branchChanged) {    return null;  }  return {    ...(modelSelectionChanged ? { modelSelection: nextModelSelection } : {}),    ...(branchChanged ? { branch: input.nextBranch, worktreePath: null } : {}),  };} export function buildLocalDraftThread(  threadId: ThreadId,  draftThread: DraftThreadState,  fallbackModelSelection: ModelSelection,): Thread {  const timestamp = DateTime.makeUnsafe(draftThread.createdAt);  return presentThreadShell(draftThread.environmentId, {    id: threadId,    projectId: draftThread.projectId,    title: "New thread",    providerInstanceId: fallbackModelSelection.instanceId,    modelSelection: fallbackModelSelection,    runtimeMode: draftThread.runtimeMode,    interactionMode: draftThread.interactionMode,    branch: draftThread.branch,    worktreePath: draftThread.worktreePath,    activeProviderThreadId: null,    lineage: { rootThreadId: threadId, parentThreadId: null, relationshipToParent: null },    forkedFrom: null,    createdBy: "user",    creationSource: "web",    latestRunId: null,    activeRunId: null,    status: "idle",    pendingRuntimeRequest: null,    latestVisibleMessage: null,    latestUserMessageAt: null,    hasActionableProposedPlan: false,    itemCount: 0,    visibleItemCount: 0,    createdAt: timestamp,    updatedAt: timestamp,    archivedAt: null,    settledOverride: null,    settledAt: null,    deletedAt: null,  });} export function shouldWriteThreadErrorToCurrentServerThread(input: {  serverThread:    | {        environmentId: EnvironmentId;        id: ThreadId;      }    | null    | undefined;  routeThreadRef: ScopedThreadRef;  targetThreadId: ThreadId;}): boolean {  return Boolean(    input.serverThread &&    input.targetThreadId === input.routeThreadRef.threadId &&    input.serverThread.environmentId === input.routeThreadRef.environmentId &&    input.serverThread.id === input.targetThreadId,  );} /** Use the same enabled instance for the composer, provider status, and chat actions. */export function resolveComposerProviderSelection(input: {  entries: ReadonlyArray<ProviderInstanceEntry>;  candidateInstanceIds: ReadonlyArray<ProviderInstanceId | null | undefined>;  lockedProvider: ProviderDriverKind | null;  lockedInstanceId: ProviderInstanceId | null | undefined;}) {  const requestedInstanceId = input.candidateInstanceIds.find(    (candidate) => candidate != null && candidate !== NO_PROVIDER_MODEL_SELECTION.instanceId,  );  const requestedDriverKind =    input.lockedProvider ??    input.entries.find((entry) => entry.instanceId === requestedInstanceId)?.driverKind ??    input.entries[0]?.driverKind ??    ProviderDriverKind.make("unconfigured");  const lockedContinuationGroupKey = input.lockedProvider    ? (input.entries.find((entry) => entry.instanceId === input.lockedInstanceId)        ?.continuationGroupKey ?? null)    : null;  // Missing metadata must not move Antigravity history into another Google profile.  const requiresExactInstance =    input.lockedProvider === "antigravity" &&    input.lockedInstanceId != null &&    lockedContinuationGroupKey === null;  const compatibleEntries = input.entries.filter(    (entry) =>      (!input.lockedProvider || entry.driverKind === input.lockedProvider) &&      (!lockedContinuationGroupKey || entry.continuationGroupKey === lockedContinuationGroupKey) &&      (!requiresExactInstance || entry.instanceId === input.lockedInstanceId),  );  const selectedProviderEntry =    input.candidateInstanceIds      .map((candidate) =>        compatibleEntries.find(          (entry) => entry.instanceId === candidate && entry.enabled && entry.isAvailable,        ),      )      .find((entry) => entry !== undefined) ??    resolveSelectableProviderInstanceEntry(      compatibleEntries.filter((entry) => entry.driverKind === requestedDriverKind),      undefined,    ) ??    resolveSelectableProviderInstanceEntry(compatibleEntries, undefined);  const unavailableProviderInstanceId = selectedProviderEntry    ? undefined    : input.lockedProvider      ? (input.lockedInstanceId ?? requestedInstanceId)      : requestedInstanceId;  return {    selectedProviderEntry,    requestedDriverKind,    lockedContinuationGroupKey,    unavailableProviderInstanceId,  };} /** Keep restored drafts and every plan control on the selected instance's supported mode. */export function resolveComposerInteractionMode(input: {  planModeEnabled: boolean;  provider: Pick<ServerProvider, "showInteractionModeToggle"> | null | undefined;  interactionMode: ProviderInteractionMode;}): { enabled: boolean; interactionMode: ProviderInteractionMode } {  const enabled =    input.planModeEnabled &&    input.provider != null &&    input.provider.showInteractionModeToggle !== false;  return {    enabled,    interactionMode: enabled ? input.interactionMode : "default",  };} export function getAntigravitySendBlockReason(  provider:    | Pick<ServerProvider, "driver" | "installed" | "auth" | "models" | "status">    | null    | undefined,  model: string,): string | null {  if (provider?.driver !== "antigravity") return null;  if (!provider.installed) {    return "Install Antigravity in provider settings before sending.";  }  if (provider.auth.status === "unauthenticated") {    return "Sign in to Antigravity in provider settings before sending.";  }  const slug = model.trim();  if (slug.length === 0) return "Choose an Antigravity model before sending.";  // A restart clears the account status and catalog. Session startup checks  // saved credentials and validates the model before sending the prompt.  if (provider.auth.status === "unknown") return null;  if (provider.models.length === 0) {    return "Refresh Antigravity models in provider settings before sending.";  }  // A saved model that left the catalog is kept in the picker as unavailable  // so the user sees what the thread used. The server rejects it at turn  // start, so block here unless the provider is in an error state, where a  // retry with the same model is the right move.  if (    provider.status === "ready" &&    slug !== ANTIGRAVITY_DEFAULT_MODEL &&    !provider.models.some((entry) => entry.slug === slug || entry.aliases?.includes(slug))  ) {    return "That Antigravity model is no longer available. Choose another model.";  }  return null;} export function reconcileMountedTerminalThreadIds(input: {  currentThreadIds: ReadonlyArray<string>;  openThreadIds: ReadonlyArray<string>;  activeThreadId: string | null;  activeThreadTerminalOpen: boolean;  maxHiddenThreadCount?: number;}): string[] {  return reconcileRetainedMountedThreadIds({    currentThreadIds: input.currentThreadIds,    openThreadIds: input.openThreadIds,    activeThreadId: input.activeThreadId,    activeThreadOpen: input.activeThreadTerminalOpen,    maxHiddenThreadCount: input.maxHiddenThreadCount ?? MAX_HIDDEN_MOUNTED_TERMINAL_THREADS,  });} export function reconcileRetainedMountedThreadIds(input: {  currentThreadIds: ReadonlyArray<string>;  openThreadIds: ReadonlyArray<string>;  activeThreadId: string | null;  activeThreadOpen: boolean;  maxHiddenThreadCount: number;  retainInactiveActiveThread?: boolean;}): string[] {  const openThreadIdSet = new Set(input.openThreadIds);  const hiddenThreadIds = input.currentThreadIds.filter(    (threadId) =>      (threadId !== input.activeThreadId || input.retainInactiveActiveThread === true) &&      openThreadIdSet.has(threadId),  );  const maxHiddenThreadCount = Math.max(0, input.maxHiddenThreadCount);  const nextThreadIds =    hiddenThreadIds.length > maxHiddenThreadCount

Lines 1–500 of 966.