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
188 lines
6.7 KiB
TypeScript
188 lines
6.7 KiB
TypeScript
import {
|
|
resolveAgentActivityCapability,
|
|
resolveAgentActivityUsage,
|
|
selectComposerOptions,
|
|
selectComposerOptionsLoadStatus,
|
|
selectComposerOptionsSectionLoadStatus,
|
|
type AgentActivityUsage,
|
|
type CanonicalAgentSession,
|
|
type AgentSessionEngine
|
|
} from "@tutti-os/agent-activity-core";
|
|
import { useMemo, useRef } from "react";
|
|
import type {
|
|
AgentSessionComposerSettings,
|
|
AgentSessionReasoningEffort,
|
|
AgentSessionState
|
|
} from "../../../shared/agentSessionTypes";
|
|
import type { AgentGUINodeData } from "../../../types";
|
|
import { composerSettingsSupportFromOptions } from "../model/composerSettingsSupport";
|
|
import { normalizeOptionalText } from "./agentGuiController.promptHelpers";
|
|
import {
|
|
composerTargetDataForConversation,
|
|
type AgentGUIComposerTargetData,
|
|
type OptimisticComposerTarget
|
|
} from "./agentGuiController.composerPresentation";
|
|
import { resolvePromptImageSelectedModel } from "./agentGuiController.draftMessageHelpers";
|
|
import { useEngineSelector } from "../../../shared/engine/useEngineSelector";
|
|
|
|
interface UseAgentGUIComposerCapabilitiesInput {
|
|
activeConversationId: string | null;
|
|
activeEngineSession: CanonicalAgentSession | null;
|
|
activeSessionState: AgentSessionState | null;
|
|
data: AgentGUINodeData;
|
|
draftSettingsBySessionId: Record<string, AgentSessionComposerSettings>;
|
|
optimisticComposerTarget?: OptimisticComposerTarget | null;
|
|
selectedComposerTargetData: AgentGUIComposerTargetData;
|
|
sessionEngine: AgentSessionEngine;
|
|
}
|
|
|
|
export function useAgentGUIComposerCapabilities(
|
|
input: UseAgentGUIComposerCapabilitiesInput
|
|
) {
|
|
const retainedUsageBySessionIdRef = useRef(
|
|
new Map<string, AgentActivityUsage>()
|
|
);
|
|
const composerTargetData = composerTargetDataForConversation({
|
|
activeConversationId: input.activeConversationId,
|
|
activeSessionTarget: input.activeEngineSession,
|
|
data: input.data,
|
|
optimisticTarget: input.optimisticComposerTarget ?? null,
|
|
selectedTarget: input.selectedComposerTargetData
|
|
});
|
|
const composerTargetKey = composerTargetData.agentTargetId?.trim() ?? "";
|
|
const providerComposerOptions = useEngineSelector(
|
|
input.sessionEngine,
|
|
(state) => selectComposerOptions(state, composerTargetKey)
|
|
);
|
|
const composerOptionsLoadStatus = useEngineSelector(
|
|
input.sessionEngine,
|
|
(state) => selectComposerOptionsLoadStatus(state, composerTargetKey)
|
|
);
|
|
const capabilitiesLoadStatus = useEngineSelector(
|
|
input.sessionEngine,
|
|
(state) =>
|
|
selectComposerOptionsSectionLoadStatus(
|
|
state,
|
|
composerTargetKey,
|
|
"capabilities"
|
|
)
|
|
);
|
|
const connectorsLoadStatus = useEngineSelector(input.sessionEngine, (state) =>
|
|
selectComposerOptionsSectionLoadStatus(
|
|
state,
|
|
composerTargetKey,
|
|
"connectors"
|
|
)
|
|
);
|
|
const composerOptionsLoading = Boolean(
|
|
composerTargetKey &&
|
|
(capabilitiesLoadStatus === "loading" ||
|
|
(!providerComposerOptions && composerOptionsLoadStatus === "loading"))
|
|
);
|
|
const connectorOptionsLoading = Boolean(
|
|
composerTargetKey && connectorsLoadStatus === "loading"
|
|
);
|
|
const defaultReasoningEffort: AgentSessionReasoningEffort | null = "high";
|
|
const sessionCapabilities = input.activeEngineSession?.capabilities ?? null;
|
|
const resolvedPromptImagesSupported =
|
|
sessionCapabilities?.imageInput ??
|
|
resolveAgentActivityCapability("imageInput", {
|
|
composerOptions: providerComposerOptions,
|
|
sessionCapabilities
|
|
});
|
|
const selectedModelForPromptImages =
|
|
resolvePromptImageSelectedModel({
|
|
activeConversationId: input.activeConversationId,
|
|
activeSessionRuntimeContext: null,
|
|
activeSessionSettings: input.activeSessionState?.settings ?? null,
|
|
activeSessionPermissionModeId: input.activeSessionState?.permissionModeId,
|
|
data: input.data,
|
|
defaultReasoningEffort,
|
|
draftSettingsBySessionId: input.draftSettingsBySessionId,
|
|
providerComposerOptions,
|
|
selectedComposerTargetData: input.selectedComposerTargetData
|
|
}) ??
|
|
normalizeOptionalText(providerComposerOptions?.effectiveSettings?.model);
|
|
const modelImageInputRequired = Boolean(
|
|
resolveAgentActivityCapability("modelImageInputRequired", {
|
|
composerOptions: providerComposerOptions,
|
|
sessionCapabilities
|
|
})
|
|
);
|
|
const selectedModelImageInputSupported = !modelImageInputRequired
|
|
? true
|
|
: selectedModelForPromptImages !== null &&
|
|
(providerComposerOptions?.models.find(
|
|
(option) => option.value === selectedModelForPromptImages
|
|
)?.supportsImageInput ??
|
|
false);
|
|
const composerSupport = useMemo(() => {
|
|
const fallback = composerSettingsSupportFromOptions(
|
|
providerComposerOptions,
|
|
sessionCapabilities
|
|
);
|
|
const targetSupport = composerSettingsSupportFromOptions(
|
|
providerComposerOptions,
|
|
null
|
|
);
|
|
return {
|
|
...fallback,
|
|
browser:
|
|
sessionCapabilities?.browserUse === true || targetSupport.browser,
|
|
computer:
|
|
sessionCapabilities?.computerUse === true || targetSupport.computer,
|
|
permissionModeChangeDeferred:
|
|
sessionCapabilities?.permissionModeChangeDeferred ??
|
|
fallback.permissionModeChangeDeferred,
|
|
permissionModeChangeDuringTurn:
|
|
sessionCapabilities?.permissionModeChangeDuringTurn ??
|
|
fallback.permissionModeChangeDuringTurn,
|
|
plan: sessionCapabilities?.planMode ?? fallback.plan,
|
|
planImplementation:
|
|
sessionCapabilities?.planImplementation ?? fallback.planImplementation
|
|
};
|
|
}, [providerComposerOptions, sessionCapabilities]);
|
|
|
|
const usageSource = input.activeEngineSession?.usage ?? null;
|
|
const usage = useMemo(() => {
|
|
const agentSessionId =
|
|
input.activeEngineSession?.agentSessionId.trim() ?? "";
|
|
if (!agentSessionId) {
|
|
return null;
|
|
}
|
|
const resolved = resolveAgentActivityUsage({
|
|
sessionUsage: usageSource
|
|
});
|
|
if (resolved) {
|
|
retainedUsageBySessionIdRef.current.set(agentSessionId, resolved);
|
|
return resolved;
|
|
}
|
|
return retainedUsageBySessionIdRef.current.get(agentSessionId) ?? null;
|
|
}, [input.activeEngineSession?.agentSessionId, usageSource]);
|
|
|
|
return {
|
|
compactSupported:
|
|
sessionCapabilities?.compact ??
|
|
resolveAgentActivityCapability("compact", {
|
|
composerOptions: providerComposerOptions,
|
|
sessionCapabilities
|
|
}),
|
|
composerSupport,
|
|
composerOptionsLoadStatus,
|
|
composerOptionsLoading,
|
|
connectorOptionsLoading,
|
|
composerTargetData,
|
|
defaultReasoningEffort,
|
|
goalPauseSupported:
|
|
resolveAgentActivityCapability("goalPause", {
|
|
composerOptions: providerComposerOptions,
|
|
sessionCapabilities
|
|
}) ?? false,
|
|
promptImagesSupported:
|
|
(resolvedPromptImagesSupported ?? true) &&
|
|
selectedModelImageInputSupported,
|
|
providerComposerOptions,
|
|
usage
|
|
};
|
|
}
|