Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

783 lines
25 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
createAgentSessionFamilySnapshotSelector,
selectEngineSessionIsRespondingToInteraction,
selectWorkspaceAgentConsumerSession,
selectWorkspaceAgentConsumerSessions
} from "@tutti-os/agent-activity-core";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { useAgentHostApi } from "../../../agentActivityHost";
import { useAgentGUIRuntime } from "../../../agentActivityRuntime";
import { useAccountStore } from "../../../host/agentHostAccountStore";
import type { AgentHostUserProject } from "../../../host/agentHostApi";
import type { AgentSessionComposerSettings } from "../../../shared/agentSessionTypes";
import { useEngineSelector } from "../../../shared/engine/useEngineSelector";
import type { AgentGUIDetailViewModel } from "../model/agentGuiNodeTypes";
import {
AGENT_GUI_RUNTIME_SESSION_ORIGIN,
conversationSummaryFromAgentSession,
type AgentGUIConversationSummary
} from "../model/agentGuiConversationModel";
import {
areAgentComposerProjectPathsEqual,
normalizeAgentComposerDraftProjectPath
} from "../model/agentComposerDraftScope";
import { mergeVisibleConversations } from "./agentGuiController.conversationHelpers";
import { reuseAgentActivityDisplayStatusesIfUnchanged } from "./agentGuiController.draftMessageHelpers";
import {
getAgentGUIErrorCode,
getAgentGUIErrorMessage
} from "./agentGuiController.errors";
import {
areAgentGUIUserProjectsEqual,
readAgentGUIUserProjectMutationPending,
readAgentGUIUserProjectSnapshot,
upsertAgentGUIUserProject
} from "./agentGuiController.interactiveHelpers";
import {
EMPTY_AGENT_GUI_MESSAGES,
composerTargetDataFromProviderTarget,
isExplicitAgentGUIAgentTarget
} from "./agentGuiController.providerHelpers";
import type { UseAgentGUINodeControllerInput } from "./agentGuiController.types";
import { reportAgentGUIActiveConversationCleared } from "./agentGuiController.reporting";
import { useAgentGUIActivation } from "./useAgentGUIActivation";
import { useAgentGUIActiveMessages } from "./useAgentGUIActiveMessages";
import { useAgentGUIConversationRouting } from "./useAgentGUIConversationRouting";
import { useAgentGUIConversationSelectionController } from "./useAgentGUIConversationSelectionController";
import { useAgentGUIConversationListState } from "./useAgentGUIConversationListState";
import { useAgentGUIComposerCapabilities } from "./useAgentGUIComposerCapabilities";
import { useAgentGUIComposerOptionsSync } from "./useAgentGUIComposerOptionsSync";
import { useAgentGUIControllerRefs } from "./useAgentGUIControllerRefs";
import { useAgentGUIOperationActions } from "./useAgentGUIOperationActions";
import { useAgentGUIViewAssembly } from "./useAgentGUIViewAssembly";
import { useAgentGUIProviderCatalogSelection } from "./useAgentGUIProviderCatalogSelection";
import { useAgentGUISessionEngineState } from "./useAgentGUISessionEngineState";
import { useAgentGUISessionDetailTransport } from "./useAgentGUISessionDetailTransport";
import { useAgentGUILocalState } from "./useAgentGUILocalState";
import {
resolveAgentGUITuttiModeDraftKey,
useAgentGUITuttiModeActivation
} from "./useAgentGUITuttiModeActivation";
export {
normalizePermissionModeSemantic,
permissionConfigFromComposerOptions,
permissionModeDescription,
permissionModeLabel,
permissionModeOptions
} from "./agentGuiController.composerHelpers";
import { trackAgentGUISettingsProjectChange } from "./agentGuiProjectAnalytics";
import type { OptimisticComposerTarget } from "./agentGuiController.composerPresentation";
export * from "./agentGuiController.conversationHelpers";
export {
agentGUIConversationDiagnosticDetails,
agentGUIRuntimeSessionDiagnosticDetails,
agentGUISessionStateDiagnosticDetails,
agentGUIToolCallStatusIsWaiting,
promptRequestId
} from "./agentGuiController.diagnostics";
export * from "./agentGuiController.draftMessageHelpers";
export * from "./agentGuiController.errors";
export {
createAgentGUIConversationId,
normalizeOptionalPrompt,
normalizeOptionalText,
projectAgentGUIMessagesToTimelineItems,
recordValue,
stringPayloadValue
} from "./agentGuiController.promptHelpers";
export * from "./agentGuiController.providerHelpers";
export * from "./agentGuiController.reporting";
export {
messageFromMessageUpdate,
normalizeTimelineStatus,
normalizedPositiveNumber,
timelineItemTime
} from "./agentGuiController.sessionHelpers";
export * from "./agentGuiController.stableHelpers";
export {
filterMessagesForDetailWindowOverlay,
maxFiniteMessageVersion,
minFiniteMessageVersion,
sessionHasRenderableMessages,
windowHasTurnMissingUserPrompt
} from "./useAgentConversationMessagePaging";
export { resolveConversationSummaryById } from "./useAgentConversationSelection";
export type { ConversationIntent } from "./useAgentConversationSelection";
export type { AgentGUIOpenSessionRequest } from "./agentGuiController.draftMessageHelpers";
export type { AgentGUIPrefillPromptRequest } from "./useAgentGUIConversationHome";
export type { AgentGUIComposerAppendRequest } from "./useAgentGUIComposerAppendRequest";
export type {
AgentGUIRememberComposerDefaultsInput,
AgentGUIRememberComposerDefaultsResult
} from "./agentGuiController.providerHelpers";
export function useAgentGUINodeController({
nodeId,
isSurfaceActive,
isSurfaceVisible,
workspaceId,
currentUserId,
workspacePath,
avoidGroupingEdits,
data,
agentTargets,
agentTargetsLoading = false,
handoffAgentTargets,
handoffAgentTargetsLoading = false,
providerRailMode = "catalog",
comingSoonProviders,
providerReadinessGates = null,
targetConnectionSource = null,
interactionReadinessSource = null,
observationGapSource = null,
defaultAgentTargetId = null,
composerAppendRequest = null,
openSessionRequest = null,
prefillPromptRequest = null,
codexSaverModeEntryEnabled = false,
rtkSaverModeEntryEnabled = false,
onDataChange,
onComposerAppendHandled,
onRememberComposerDefaults,
onShowMessage
}: UseAgentGUINodeControllerInput) {
const agentActivityRuntime = useAgentGUIRuntime();
const agentActivityRuntimeOrigin =
agentActivityRuntime.origin?.trim() || AGENT_GUI_RUNTIME_SESSION_ORIGIN;
const sessionEngine = useMemo(() => {
const engine = agentActivityRuntime.getSessionEngine(workspaceId);
if (
engine.identity.workspaceId !== workspaceId ||
engine.identity.origin !== agentActivityRuntimeOrigin
) {
throw new Error(
"Agent activity runtime returned a session engine for a different identity."
);
}
return engine;
}, [agentActivityRuntime, agentActivityRuntimeOrigin, workspaceId]);
// Stable runtime identity isolates conversation queries and session-view refs.
const agentHostApi = useAgentHostApi();
const providerCatalogSelection = useAgentGUIProviderCatalogSelection({
comingSoonProviders,
data,
defaultAgentTargetId,
providerRailMode,
providerReadinessGates,
agentTargets,
agentTargetsLoading,
handoffAgentTargets,
handoffAgentTargetsLoading
});
const {
effectiveSelectedProviderTarget,
homeComposerTargetOverride,
normalizedComingSoonProviders,
normalizedExplicitProviderTargets,
normalizedProviderTargets,
selectedComposerTargetData,
selectedAgentTargetIsExplicit,
setHomeComposerTargetOverride
} = providerCatalogSelection;
const localState = useAgentGUILocalState({
data,
userProjectsApi: agentHostApi.userProjects
});
const {
activeConversationId,
clearRailRevealRequest,
draftByScopeKey,
draftSettingsBySessionId,
intent,
isComposerHome,
selectedProjectPath,
requestRailReveal,
setActiveConversationId,
setDetailError,
setIntent,
setIsComposerHome,
setIsLoadingMessages,
setIsUserProjectMutationPending,
setSelectedProjectPath,
setUserProjects,
userProjects
} = localState;
const agentActivityDisplayStatuses = useEngineSelector(
sessionEngine,
(state) => {
const statuses = new Map(
selectWorkspaceAgentConsumerSessions(state).map((item) => [
item.session.agentSessionId,
item.displayStatus
])
);
if (activeConversationId && !statuses.has(activeConversationId)) {
const activeConsumer = selectWorkspaceAgentConsumerSession(
state,
activeConversationId
);
if (activeConsumer) {
statuses.set(activeConversationId, activeConsumer.displayStatus);
}
}
return statuses;
},
(left, right) =>
reuseAgentActivityDisplayStatusesIfUnchanged(left, right) === left
);
const activeAgentActivityDisplayStatus = activeConversationId
? (agentActivityDisplayStatuses.get(activeConversationId) ?? null)
: null;
const tuttiModeDraftKey = useMemo(
() => resolveAgentGUITuttiModeDraftKey(nodeId),
[nodeId]
);
const tuttiModeActivation = useAgentGUITuttiModeActivation({
activeConversationId,
draftKey: tuttiModeDraftKey,
engine: sessionEngine,
workspaceId
});
const conversationList = useAgentGUIConversationListState({
agentActivityRuntimeOrigin,
currentUserId,
data,
normalizedProviderTargets,
sessionEngine,
workspaceId
});
const {
attentionReadState,
conversationFilter,
conversationListQuery,
conversationListState,
conversations
} = conversationList;
const hasLoadedConversations = conversationListState?.initialized ?? false;
const isLoadingConversations = conversationListState?.isLoading ?? false;
const sessionEngineState = useAgentGUISessionEngineState({
activeConversationId,
sessionEngine
});
const {
activeEngineSession,
activePendingActivation,
activePendingSubmits,
activeQueuedPrompts,
activeSessionState,
isCreatingConversation
} = sessionEngineState;
const selectActiveSessionFamily = useMemo(
() => createAgentSessionFamilySnapshotSelector(activeConversationId),
[activeConversationId]
);
const activeSessionFamily = useEngineSelector(
sessionEngine,
selectActiveSessionFamily
);
const activeRelatedPendingInteractions =
activeSessionFamily.pendingInteractions;
const activeRelatedIsRespondingToInteraction = useEngineSelector(
sessionEngine,
(state) =>
activeRelatedPendingInteractions.some((interaction) =>
selectEngineSessionIsRespondingToInteraction(
state,
interaction.agentSessionId
)
)
);
const optimisticComposerTarget =
useMemo<OptimisticComposerTarget | null>(() => {
if (
!isCreatingConversation ||
activePendingActivation?.mode !== "new" ||
activePendingActivation.agentSessionId !== activeConversationId ||
activePendingActivation.agentTargetId !==
selectedComposerTargetData.agentTargetId
) {
return null;
}
return {
agentSessionId: activePendingActivation.agentSessionId,
target: selectedComposerTargetData
};
}, [
activeConversationId,
activePendingActivation,
isCreatingConversation,
selectedComposerTargetData
]);
// Bridges submitInteractivePrompt
// updateComposerSettings (defined later); assigned right after the
// callback's definition.
const updateComposerSettingsRef = useRef<
(nextSettings: Partial<AgentSessionComposerSettings>) => void
>(() => {});
// Bridges submitInteractivePrompt (defined earlier) to the client-side plan
// decision handlers (defined later); assigned after those callbacks.
const planActionsRef = useRef<{
implement: () => boolean;
feedback: (text: string) => boolean;
skip: () => boolean;
}>({ implement: () => false, feedback: () => false, skip: () => false });
const composerCapabilities = useAgentGUIComposerCapabilities({
activeConversationId,
activeEngineSession,
activeSessionState,
data,
draftSettingsBySessionId,
optimisticComposerTarget,
selectedComposerTargetData,
sessionEngine
});
const {
composerSupport,
composerTargetData,
defaultReasoningEffort,
providerComposerOptions
} = composerCapabilities;
const planImplementationTurnIdRef = useRef<string | null>(null);
const accountProfilesByUserId = useAccountStore(
(state) => state.profilesByUserId
);
const controllerRefs = useAgentGUIControllerRefs({
activeConversationId,
conversations,
data,
draftByScopeKey,
draftSettingsBySessionId,
effectiveSelectedProviderTarget,
homeComposerTargetOverride,
isComposerHome,
isCreatingConversation,
onDataChange,
onRememberComposerDefaults,
onShowMessage,
agentTargetsProvided: agentTargets !== undefined,
selectedComposerTargetData,
selectedProjectPath,
selectedAgentTargetIsExplicit,
userProjects
});
const {
activeConversationIdRef,
conversationIdsRef,
conversationsRef,
dataRef,
draftSettingsBySessionIdRef,
handledOpenSessionSequenceRef,
isComposerHomeRef,
isMountedRef,
loadDraftComposerOptionsRef,
onDataChangeRef,
onComposerDefaultsAuthorityReloadedRef,
pendingOpenSessionRequestRef,
selectedComposerTargetDataRef,
selectedProjectPathRef,
userProjectsLoadSeqRef,
userProjectsRef
} = controllerRefs;
const sessionDetailTransport = useAgentGUISessionDetailTransport({
activeConversationId,
activeConversationIdRef,
agentActivityRuntime,
agentActivityRuntimeOrigin,
dataRef,
isMountedRef,
sessionFamily: activeSessionFamily,
sessionEngine,
workspaceId
});
const {
loadSelectedConversationMessages,
loadSessionState,
markSelectedConversationDetailPending,
resolveSessionMessages,
setActiveMessageSession
} = sessionDetailTransport;
const storedActiveMessages = activeConversationId
? resolveSessionMessages(activeConversationId)
: EMPTY_AGENT_GUI_MESSAGES;
const { activeMessages, activeTimelineItems } = useAgentGUIActiveMessages({
activeConversationId,
activePendingActivation,
activePendingSubmits,
activeQueuedPrompts,
currentUserId,
storedMessages: storedActiveMessages,
workspaceId
});
const transientConversation =
useMemo<AgentGUIConversationSummary | null>(() => {
const session = activeEngineSession;
if (
!session ||
conversations.some(
(conversation) => conversation.id === session.agentSessionId
)
) {
return null;
}
return {
...conversationSummaryFromAgentSession(session, {
needsUserAction: activeRelatedPendingInteractions.length > 0,
userProjects
}),
isTransient: true
};
}, [
activeEngineSession,
activeRelatedPendingInteractions.length,
agentActivityDisplayStatuses,
conversations,
userProjects
]);
// Stashes the error message from a failed first-message create so the
// activeConversationId-null effect (which otherwise clears detailError on
// every home transition) can surface it on the home composer instead of
// wiping it out during the optimistic-entry revert.
const activation = useAgentGUIActivation({
engine: sessionEngine,
workspaceId,
getErrorMessage: getAgentGUIErrorMessage,
getErrorCode: getAgentGUIErrorCode
});
const activeConversationLiveState = activation.stateFor(activeConversationId);
const setUserProjectsSnapshot = useCallback(
(projects: readonly AgentHostUserProject[]) => {
setUserProjects((current) =>
areAgentGUIUserProjectsEqual(current, projects)
? current
: [...projects]
);
},
[]
);
useEffect(() => {
const api = agentHostApi.userProjects;
let disposed = false;
setUserProjectsSnapshot(readAgentGUIUserProjectSnapshot(api));
setIsUserProjectMutationPending(
readAgentGUIUserProjectMutationPending(api)
);
const loadUserProjects = async () => {
const requestSeq = ++userProjectsLoadSeqRef.current;
if (!api) {
if (!disposed && requestSeq === userProjectsLoadSeqRef.current) {
setUserProjectsSnapshot([]);
}
return;
}
try {
const result = await api.list();
if (!disposed && requestSeq === userProjectsLoadSeqRef.current) {
setUserProjectsSnapshot(result.projects);
}
} catch {
if (!disposed && requestSeq === userProjectsLoadSeqRef.current) {
setUserProjectsSnapshot([]);
}
}
};
void loadUserProjects();
const unsubscribe = api?.subscribe?.(() => {
setIsUserProjectMutationPending(
readAgentGUIUserProjectMutationPending(api)
);
void loadUserProjects();
});
return () => {
disposed = true;
unsubscribe?.();
};
}, [
agentHostApi.userProjects,
setIsUserProjectMutationPending,
setUserProjectsSnapshot
]);
// NOTE: project metadata is intentionally NOT written back into the shared
// conversation store. `conversation.project` is a per-window exact JOIN of
// railSectionKey × userProjects.sectionKey; deriving it here and persisting it
// caused cross-window update storms. Rail membership comes from the same
// backend railSectionKey contract.
useEffect(() => {
if (activeConversationId === null && isComposerHome) {
return;
}
setHomeComposerTargetOverride(null);
}, [activeConversationId, isComposerHome]);
const conversationSelection = useAgentGUIConversationSelectionController({
activation,
activeConversationId,
activeConversationIdRef,
activePendingActivation,
activeSessionReconcileErrorCode:
sessionEngineState.activeSessionReconcileErrorCode,
agentActivityRuntime,
attentionReadRecordsBySessionId: attentionReadState.recordsBySessionId,
conversationIdsRef,
conversationsRef,
conversationListQuery,
clearRailRevealRequest,
currentUserId,
data,
dataRef,
intent,
isComposerHomeRef,
isMountedRef,
isSurfaceActive,
isSurfaceVisible,
loadDraftComposerOptions: () => loadDraftComposerOptionsRef.current(),
loadSelectedConversationMessages,
loadSessionState,
markSelectedConversationDetailPending,
nodeId,
onDataChangeRef,
sessionEngine,
requestRailReveal,
setActiveConversationId,
setDetailError,
setIntent,
setIsComposerHome,
setIsLoadingMessages,
setActiveMessageSession,
transientConversation,
workspaceId
});
const persistActiveConversation =
conversationSelection.persistActiveConversation;
const selectConversation = conversationSelection.selectConversation;
const syncConversationListProjection =
conversationSelection.syncConversationListProjection;
const updateSelectedProjectPath = useCallback(
(
path: string | null,
metadata?: {
action: "clear" | "create_new" | "import_directory" | "select_existing";
project?: {
id: string;
path: string;
label: string;
sectionKey?: string;
createdAtUnixMs?: number;
updatedAtUnixMs?: number;
lastUsedAtUnixMs?: number | null;
pinnedAtUnixMs: number;
};
}
) => {
const normalizedPath = normalizeAgentComposerDraftProjectPath(path);
const project = metadata?.project;
if (
project &&
normalizedPath &&
areAgentComposerProjectPathsEqual(project.path, normalizedPath)
) {
const nextProjects = upsertAgentGUIUserProject(
userProjectsRef.current,
project
);
userProjectsRef.current = nextProjects;
setUserProjectsSnapshot(nextProjects);
}
selectedProjectPathRef.current = normalizedPath;
setSelectedProjectPath(normalizedPath);
trackAgentGUISettingsProjectChange({
agentActivityRuntime,
agentSessionId: activeConversationIdRef.current,
metadata,
provider: dataRef.current.provider,
workspaceId
});
},
[agentActivityRuntime, workspaceId]
);
useEffect(() => {
if (!hasLoadedConversations) {
return;
}
const nextConversationCount = mergeVisibleConversations(
conversations,
transientConversation
).filter((conversation) => !conversation.hiddenFromRail).length;
onDataChangeRef.current((current) =>
current.conversationCount === nextConversationCount
? current
: { ...current, conversationCount: nextConversationCount }
);
}, [conversations.length, hasLoadedConversations, transientConversation]);
useAgentGUIConversationRouting({
activeConversationIdRef,
conversationListQuery,
conversations,
conversationsRef,
handledOpenSessionSequenceRef,
hasLoadedConversations,
intent,
openSessionRequest,
pendingOpenSessionRequestRef,
selectConversation,
sessionEngine,
setIntent,
transientConversation,
workspaceId
});
const { loadDraftComposerOptions, reloadComposerOptionsForTarget } =
useAgentGUIComposerOptionsSync({
activeConversationId,
activeConversationIdRef,
activeSessionTarget: activeEngineSession,
agentActivityRuntime,
composerTargetData,
conversationFilter,
currentUserId,
data,
dataRef,
defaultReasoningEffort,
draftSettingsBySessionIdRef,
isComposerHome,
isComposerHomeRef,
isCreatingConversation,
loadDraftComposerOptionsRef,
loadSessionState,
onComposerDefaultsAuthorityReloadedRef,
optimisticComposerTarget,
providerComposerOptions,
selectedComposerTargetDataRef,
selectedProjectPath,
selectedProjectPathRef,
syncConversationListProjection,
workspaceId,
workspacePath
});
const operationActions = useAgentGUIOperationActions({
...providerCatalogSelection,
...localState,
...conversationList,
...sessionEngineState,
...composerCapabilities,
...controllerRefs,
...sessionDetailTransport,
...conversationSelection,
activeEnginePendingInteractions: activeRelatedPendingInteractions,
accountProfilesByUserId,
activation,
agentActivityRuntime,
agentHostApi,
composerTargetDataFromProviderTarget,
composerAppendRequest,
codexSaverModeEntryEnabled,
rtkSaverModeEntryEnabled,
onComposerAppendHandled,
composerSupportPermissionModeChangeDeferred:
composerSupport.permissionModeChangeDeferred,
currentProvider: data.provider,
currentUserId,
data,
defaultAgentTargetId,
isExplicitAgentGUIAgentTarget,
isRespondingToInteraction: activeRelatedIsRespondingToInteraction,
loadDraftComposerOptions,
reloadComposerOptionsForTarget,
normalizedExplicitProviderTargets,
normalizedProviderTargets,
planActionsRef,
planImplementationTurnIdRef,
interactionReadinessSource,
prefillPromptRequest,
reportActiveConversationCleared: reportAgentGUIActiveConversationCleared,
sessionEngine,
setUserProjectsSnapshot,
transientConversation,
tuttiModeDraftKey,
unactivate: activation.unactivate,
updateComposerSettingsRef,
workspaceId
});
const isLoadingMessages =
localState.isLoadingMessages ||
sessionEngineState.activeSessionDetailLoading;
const detailAvailability: AgentGUIDetailViewModel["availability"] =
activeConversationId === null
? "ready"
: sessionEngineState.activeEngineSessionDeleted
? "not_found"
: isLoadingMessages
? "loading"
: sessionEngineState.activeSessionReconcileError ||
localState.detailError
? "error"
: "ready";
const viewAssembly = useAgentGUIViewAssembly({
...providerCatalogSelection,
...localState,
...conversationList,
...sessionEngineState,
...composerCapabilities,
...controllerRefs,
...sessionDetailTransport,
...conversationSelection,
...operationActions,
activeCancelStatus: sessionEngineState.activeCancelState?.status ?? null,
activePendingInteractions: activeRelatedPendingInteractions,
activeTurn: sessionEngineState.activeEngineActiveTurn,
activeLatestPendingSubmitTurnId:
sessionEngineState.activeLatestPendingSubmit?.turnId ?? null,
activeMessages,
activeSessionFamily,
activeTimelineItems,
activeEngineHasPendingInteractions:
activeRelatedPendingInteractions.length > 0,
isRespondingToInteraction: activeRelatedIsRespondingToInteraction,
activeConversationLiveState,
activationState: activeConversationLiveState,
activityDisplayStatus: activeAgentActivityDisplayStatus,
activityDisplayStatuses: agentActivityDisplayStatuses,
agentActivityRuntime,
avoidGroupingEdits,
currentUserId,
codeFor: activation.codeFor,
composerTargetProvider: composerTargetData.provider,
codexSaverModeEntryEnabled,
rtkSaverModeEntryEnabled,
data,
defaultAgentTargetId,
errorFor: activation.errorFor,
detailAvailability,
isCreatingConversation,
isLoadingConversations,
isLoadingMessages,
normalizedComingSoonProviders,
nodeId,
operationActions,
persistActiveConversation,
planImplementationTurnIdRef,
providerRailMode,
providerReadinessGates,
targetConnectionSource,
interactionReadinessSource,
observationGapSource,
agentTargetsLoading,
selectedComposerTargetData,
sessionEngine,
transientConversation,
tuttiModeActivation,
unactivate: activation.unactivate,
updateSelectedProjectPath,
userProjects,
workspaceId,
workspacePath
});
return viewAssembly;
}