Files
tutti/packages/agent/gui/agent-gui/agentGuiNode/controller/useAgentGUISubmitInteractionActions.ts
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

799 lines
28 KiB
TypeScript

import {
type AgentActivityGoalControlAction,
type AgentActivityInteraction,
type AgentActivityTurn,
type AgentSessionEngine
} from "@tutti-os/agent-activity-core";
import type { Dispatch, RefObject, SetStateAction } from "react";
import { useCallback, useEffect, useRef } from "react";
import type { AgentGUIRuntime } from "../../../agentActivityRuntime";
import { translate } from "../../../i18n/index";
import type { AgentPromptContentBlock } from "../../../shared/contracts/dto";
import type { AgentInteractionResponseInput } from "../../../shared/agentConversation/contracts/agentConversationVM";
import type {
AgentGUINodeData,
AgentGUIInteractionReadinessSource
} from "../../../types";
import {
agentPromptContentDisplayText,
agentPromptContentHasImage,
emptyAgentComposerDraft,
normalizeAgentPromptContentBlocks,
snapshotAgentComposerDraft
} from "../model/agentComposerDraft";
import type {
AgentComposerDraft,
SubmittedDraftSnapshot
} from "../model/agentGuiNodeTypes";
import { resolveAgentComposerDraftScopeKey } from "../model/agentComposerDraftScope";
import type { AgentGUIConversationSummary } from "../model/agentGuiConversationModel";
import type { AgentComposerSubmitOptions } from "../composer/AgentComposer.types";
import {
PLAN_IMPLEMENTATION_ACTION_FEEDBACK,
PLAN_IMPLEMENTATION_ACTION_IMPLEMENT,
PLAN_IMPLEMENTATION_ACTION_SKIP
} from "../../../shared/agentConversation/planImplementationPresentation";
import {
clearSubmittedDraftIfUnchanged,
deleteUnacceptedSubmittedDraftSnapshot,
toRuntimeSendContent
} from "./agentGuiController.draftMessageHelpers";
import { clearSubmittedAgentGUIHomeDraft } from "./agentGuiController.homeDraftHelpers";
import {
agentGUIInteractionDraftSettlementKey,
AgentGUIEngineSettlementController,
type AgentGUIGoalControlPendingSettlement,
type AgentGUIInteractionDraftPendingSettlement
} from "./AgentGUIEngineSettlementController";
import {
AGENT_RESUME_SESSION_NOT_LOCAL_ERROR,
agentGUISubmitSettlementError,
buildProviderSessionNotFoundActivationError,
buildResumeSessionNotLocalActivationError,
getAgentGUIErrorMessage,
goalControlSettlementError,
isNonRetryableResumeErrorCode
} from "./agentGuiController.errors";
import { typedGoalControlFromComposer } from "./agentGuiController.composerHelpers";
import {
agentSubmitTraceDiagnostics,
createAgentSubmitTraceState,
reportAgentGUISubmitRecoveredActiveConversation,
reportAgentGUISubmitWithoutActiveConversation,
reportAgentSubmitTraceDiagnostic,
scheduleAgentSubmitTracePaint
} from "./agentGuiController.reporting";
import { resolveAgentGUIInteractionReadinessIdentity } from "./agentGuiController.interactionHelpers";
import { readAgentGUIInteractionReadiness } from "./useAgentGUIInteractionReadiness";
import {
resolveConversationSummaryById,
type ConversationIntent
} from "./useAgentConversationSelection";
import type { useAgentGUIActivation } from "./useAgentGUIActivation";
import type { AgentGUINewConversationActivationResult } from "./agentGuiNewConversationActivation.types";
import { useAgentGUIGoalControlActions } from "./useAgentGUIGoalControlActions";
import { resolvePendingQuestionComposerResponse } from "./pendingQuestionComposerResponse";
interface UseAgentGUISubmitInteractionActionsInput {
activation: ReturnType<typeof useAgentGUIActivation>;
activeConversationId: string | null;
activeConversationIdRef: RefObject<string | null>;
activeEngineActiveTurn: AgentActivityTurn | null;
activeEnginePendingInteractions: readonly AgentActivityInteraction[];
agentActivityRuntime: AgentGUIRuntime;
conversationListQuery: unknown | null;
conversationsRef: RefObject<AgentGUIConversationSummary[]>;
dataRef: RefObject<AgentGUINodeData>;
draftByScopeKeyRef: RefObject<Record<string, AgentComposerDraft>>;
executePromptRef: RefObject<
(
agentSessionId: string,
content: AgentPromptContentBlock[],
displayPrompt?: string,
options?: {
immediate?: boolean;
requiredSettingsPatch?: AgentComposerSubmitOptions["requiredSettingsPatch"];
sendNow?: boolean;
submittedDraft?: AgentComposerSubmitOptions["submittedDraft"];
targetTurnId?: AgentComposerSubmitOptions["targetTurnId"];
sourceScopeKey?: string;
trackDraft?: boolean;
}
) => void
>;
goalControlSupported: boolean;
isComposerHomeRef: RefObject<boolean>;
isCurrentConversation(agentSessionId: string): boolean;
isRespondingToInteraction: boolean;
interactionReadinessSource?: AgentGUIInteractionReadinessSource | null;
isSessionMarkedNonResumable(agentSessionId: string): boolean;
persistActiveConversation(agentSessionId: string | null): void;
planActionsRef: RefObject<{
implement(): boolean;
feedback(value: string): boolean;
skip(): boolean;
}>;
promptImagesSupported: boolean;
sessionEngine: AgentSessionEngine;
setActiveConversationId: Dispatch<SetStateAction<string | null>>;
setDetailError: Dispatch<SetStateAction<string | null>>;
setDraftByScopeKey: Dispatch<
SetStateAction<Record<string, AgentComposerDraft>>
>;
setGoalClearNoticeSequence: Dispatch<SetStateAction<number>>;
setIntent: Dispatch<SetStateAction<ConversationIntent>>;
submittedDraftSnapshotsRef: RefObject<Record<string, SubmittedDraftSnapshot>>;
startConversation(
content: AgentPromptContentBlock[],
displayPrompt?: string,
options?: AgentComposerSubmitOptions,
initialTurnExpected?: boolean,
initialGoalControl?: {
action: AgentActivityGoalControlAction;
objective?: string;
}
): AgentGUINewConversationActivationResult | null;
submitPromptRef: RefObject<
(
content: AgentPromptContentBlock[],
displayPrompt?: string,
options?: AgentComposerSubmitOptions
) => void
>;
transientConversation: AgentGUIConversationSummary | null;
workspaceId: string;
}
export function useAgentGUISubmitInteractionActions(
input: UseAgentGUISubmitInteractionActionsInput
) {
const {
activation,
activeConversationIdRef,
activeEngineActiveTurn,
activeEnginePendingInteractions,
agentActivityRuntime,
conversationListQuery,
conversationsRef,
dataRef,
draftByScopeKeyRef,
executePromptRef,
goalControlSupported,
isComposerHomeRef,
isCurrentConversation,
isRespondingToInteraction,
interactionReadinessSource,
isSessionMarkedNonResumable,
persistActiveConversation,
planActionsRef,
promptImagesSupported,
sessionEngine,
setActiveConversationId,
setDetailError,
setDraftByScopeKey,
setGoalClearNoticeSequence,
setIntent,
submittedDraftSnapshotsRef,
startConversation,
submitPromptRef,
transientConversation,
workspaceId
} = input;
const goalControlSettlementsRef = useRef<
Record<string, AgentGUIGoalControlPendingSettlement>
>({});
const interactionDraftSettlementsRef = useRef<
Record<string, AgentGUIInteractionDraftPendingSettlement>
>({});
const { goalControl } = useAgentGUIGoalControlActions({
activeConversationIdRef,
draftByScopeKeyRef,
goalControlSettlementsRef,
sessionEngine,
setDetailError
});
const retryActivation = useCallback(() => {
const agentSessionId = activeConversationIdRef.current;
if (!agentSessionId) {
return;
}
if (isSessionMarkedNonResumable(agentSessionId)) {
return;
}
if (isNonRetryableResumeErrorCode(activation.codeFor(agentSessionId))) {
return;
}
setDetailError(null);
activation.activate({ mode: "existing", agentSessionId });
}, [
agentActivityRuntime,
activation,
isCurrentConversation,
isSessionMarkedNonResumable,
workspaceId
]);
const executePrompt = useCallback(
(
agentSessionId: string,
content: AgentPromptContentBlock[],
displayPrompt?: string,
options?: {
capabilityRefs?: AgentComposerSubmitOptions["capabilityRefs"];
immediate?: boolean;
requiredSettingsPatch?: AgentComposerSubmitOptions["requiredSettingsPatch"];
sendNow?: boolean;
submittedDraft?: AgentComposerSubmitOptions["submittedDraft"];
targetTurnId?: AgentComposerSubmitOptions["targetTurnId"];
sourceScopeKey?: string;
trackDraft?: boolean;
}
) => {
const normalizedContent = normalizeAgentPromptContentBlocks(content);
if (!agentSessionId || normalizedContent.length === 0) {
return;
}
const targetIsActiveConversation =
activeConversationIdRef.current === agentSessionId;
// displayPrompt(如 bundle 折叠成单 chip)优先用于回显;否则回退到 content 派生文本。
const submittedPromptText =
displayPrompt && displayPrompt.trim()
? displayPrompt
: agentPromptContentDisplayText(normalizedContent);
const submittedAtUnixMs = Date.now();
const submitTrace = createAgentSubmitTraceState({
agentSessionId,
content: normalizedContent,
prompt: submittedPromptText,
queued: false,
startedAtUnixMs: submittedAtUnixMs
});
if (options?.trackDraft === true) {
const sourceScopeKey =
options.sourceScopeKey ??
resolveAgentComposerDraftScopeKey({ agentSessionId });
const submittedDraft =
options?.submittedDraft ??
draftByScopeKeyRef.current[sourceScopeKey] ??
emptyAgentComposerDraft();
submittedDraftSnapshotsRef.current[submitTrace.clientSubmitId] = {
sourceScopeKey,
content: snapshotAgentComposerDraft(submittedDraft),
targetAgentSessionId: agentSessionId
};
}
const targetConversation = resolveConversationSummaryById(
conversationsRef.current,
agentSessionId,
transientConversation
);
reportAgentSubmitTraceDiagnostic({
event: "submit.begin",
runtime: agentActivityRuntime,
trace: submitTrace,
workspaceId,
fields: {
activeConversationId: activeConversationIdRef.current,
conversationKnown: targetConversation !== null,
conversationStatus: targetConversation?.status ?? null,
isComposerHome: isComposerHomeRef.current,
targetIsActiveConversation,
targetMode: "existing"
}
});
const { accepted, queued } = sessionEngine.submitPrompt({
agentSessionId,
...(options?.capabilityRefs?.length
? { capabilityRefs: options.capabilityRefs }
: {}),
clientSubmitId: submitTrace.clientSubmitId,
content: normalizedContent,
...(displayPrompt && displayPrompt.trim() ? { displayPrompt } : {}),
submitDiagnostics: agentSubmitTraceDiagnostics(submitTrace),
...(options?.requiredSettingsPatch
? {
requiredSettingsPatch: {
...options.requiredSettingsPatch
}
}
: {}),
...(options?.targetTurnId?.trim()
? { targetTurnId: options.targetTurnId.trim() }
: {}),
...(options?.immediate === true
? { routing: "immediate" as const }
: options?.sendNow === true
? { routing: "send_now" as const }
: {}),
runtimeContent: toRuntimeSendContent(normalizedContent)
});
submitTrace.queued = queued;
setDetailError(null);
// Clear the composer optimistically the instant the engine takes the
// prompt — whether it was queued behind a busy turn or accepted straight
// into an idle session. The snapshot is retained so
// AgentGUIEngineSettlementController can restore it if the send is
// later rejected. A submit the engine never accepted is left untouched so
// its text is not lost (deleteUnacceptedSubmittedDraftSnapshot cleans up).
const submittedSnapshot =
submittedDraftSnapshotsRef.current[submitTrace.clientSubmitId];
if ((accepted || queued) && submittedSnapshot) {
setDraftByScopeKey((current) => {
const next = clearSubmittedDraftIfUnchanged({
drafts: current,
snapshot: submittedSnapshot
});
draftByScopeKeyRef.current = next;
return next;
});
}
deleteUnacceptedSubmittedDraftSnapshot({
snapshots: submittedDraftSnapshotsRef.current,
clientSubmitId: submitTrace.clientSubmitId,
accepted,
queued
});
reportAgentSubmitTraceDiagnostic({
event: "send_input.requested",
runtime: agentActivityRuntime,
trace: submitTrace,
workspaceId
});
scheduleAgentSubmitTracePaint({
runtime: agentActivityRuntime,
trace: submitTrace,
workspaceId
});
},
[agentActivityRuntime, sessionEngine, setDraftByScopeKey, workspaceId]
);
useEffect(() => {
executePromptRef.current = executePrompt;
}, [executePrompt]);
useEffect(() => {
const controller = new AgentGUIEngineSettlementController({
applyDraftUpdate: (update) => {
setDraftByScopeKey((current) => {
const next = update(current);
draftByScopeKeyRef.current = next;
return next;
});
},
engine: sessionEngine,
goalControlSettlements: goalControlSettlementsRef.current,
interactionDraftSettlements: interactionDraftSettlementsRef.current,
isCurrentConversation,
onGoalControlCleared: () =>
setGoalClearNoticeSequence((current) => current + 1),
onGoalControlFailed: (settlement) => {
setDetailError(
settlement.errorMessage
? getAgentGUIErrorMessage(goalControlSettlementError(settlement))
: translate("agentHost.agentGui.goalControlFailed")
);
},
onSubmitFailed: (submit) => {
setDetailError(
getAgentGUIErrorMessage(agentGUISubmitSettlementError(submit))
);
},
snapshots: submittedDraftSnapshotsRef.current
});
return controller.attach();
}, [
draftByScopeKeyRef,
isCurrentConversation,
sessionEngine,
setDetailError,
setDraftByScopeKey,
setGoalClearNoticeSequence,
submittedDraftSnapshotsRef
]);
const submitExistingPrompt = useCallback(
(
agentSessionId: string,
normalizedContent: AgentPromptContentBlock[],
displayPromptText?: string,
options?: {
capabilityRefs?: AgentComposerSubmitOptions["capabilityRefs"];
requiredSettingsPatch?: AgentComposerSubmitOptions["requiredSettingsPatch"];
sendNow?: boolean;
submittedDraft?: AgentComposerSubmitOptions["submittedDraft"];
targetTurnId?: AgentComposerSubmitOptions["targetTurnId"];
sourceScopeKey?: string;
trackDraft?: boolean;
}
) => {
if (isSessionMarkedNonResumable(agentSessionId)) {
setDetailError(
getAgentGUIErrorMessage(buildResumeSessionNotLocalActivationError())
);
return;
}
if (isNonRetryableResumeErrorCode(activation.codeFor(agentSessionId))) {
setDetailError(
getAgentGUIErrorMessage(
activation.codeFor(agentSessionId) ===
AGENT_RESUME_SESSION_NOT_LOCAL_ERROR
? buildResumeSessionNotLocalActivationError(
activation.errorFor(agentSessionId)
)
: buildProviderSessionNotFoundActivationError(
activation.errorFor(agentSessionId)
)
)
);
return;
}
executePrompt(agentSessionId, normalizedContent, displayPromptText, {
capabilityRefs: options?.capabilityRefs,
requiredSettingsPatch: options?.requiredSettingsPatch,
targetTurnId: options?.targetTurnId,
sendNow: options?.sendNow === true,
submittedDraft: options?.submittedDraft,
sourceScopeKey: options?.sourceScopeKey,
trackDraft: options?.trackDraft === true
});
},
[activation, executePrompt, isSessionMarkedNonResumable, workspaceId]
);
const submitInteractivePrompt = useCallback(
(input: AgentInteractionResponseInput): boolean => {
// Plan-implementation actions are client-orchestrated; route them to the
// plan decision handlers instead of submitInteractive.
if (input.action === PLAN_IMPLEMENTATION_ACTION_IMPLEMENT) {
return planActionsRef.current.implement();
}
if (input.action === PLAN_IMPLEMENTATION_ACTION_FEEDBACK) {
return planActionsRef.current.feedback(
typeof input.payload?.text === "string" ? input.payload.text : ""
);
}
if (input.action === PLAN_IMPLEMENTATION_ACTION_SKIP) {
return planActionsRef.current.skip();
}
const normalizedOptionId = input.optionId?.trim() ?? "";
const target = resolveAgentGUIInteractionReadinessIdentity({
agentSessionId: input.agentSessionId,
requestId: input.requestId,
turnId: input.turnId,
workspaceId
});
const exactPendingInteraction =
target !== null &&
activeEnginePendingInteractions.some(
(interaction) =>
interaction.status === "pending" &&
interaction.agentSessionId.trim() === target.agentSessionId &&
interaction.turnId.trim() === target.turnId &&
interaction.requestId.trim() === target.requestId
);
if (!target || !exactPendingInteraction || isRespondingToInteraction) {
return false;
}
if (
readAgentGUIInteractionReadiness({
identity: target,
source: interactionReadinessSource
})?.status === "blocked"
) {
return false;
}
setDetailError(null);
return sessionEngine.submitInteractionResponse({
...(input.action?.trim() ? { action: input.action.trim() } : {}),
agentSessionId: target.agentSessionId,
...(normalizedOptionId ? { optionId: normalizedOptionId } : {}),
...(input.payload ? { payload: { ...input.payload } } : {}),
requestId: target.requestId,
turnId: target.turnId
});
},
[
activeEnginePendingInteractions,
interactionReadinessSource,
isRespondingToInteraction,
sessionEngine,
workspaceId
]
);
const submitPrompt = useCallback(
(
content: AgentPromptContentBlock[],
displayPrompt?: string,
options?: AgentComposerSubmitOptions
) => {
const agentSessionId = activeConversationIdRef.current;
const normalizedContent = normalizeAgentPromptContentBlocks(content);
if (normalizedContent.length === 0) {
return;
}
const displayPromptText =
displayPrompt && displayPrompt.trim() ? displayPrompt : undefined;
const typedGoal = typedGoalControlFromComposer(
normalizedContent,
displayPromptText,
goalControlSupported
);
if (
!promptImagesSupported &&
agentPromptContentHasImage(normalizedContent)
) {
setDetailError(translate("agentHost.agentGui.promptImagesUnsupported"));
return;
}
if (!agentSessionId) {
if (!isComposerHomeRef.current) {
const promptLength =
agentPromptContentDisplayText(normalizedContent).length;
reportAgentGUISubmitWithoutActiveConversation({
blockCount: normalizedContent.length,
conversationCount: conversationsRef.current.length,
conversationListQueryReady: conversationListQuery !== null,
dataLastActiveAgentSessionId:
dataRef.current.lastActiveAgentSessionId ?? null,
isComposerHome: isComposerHomeRef.current,
promptLength,
provider: dataRef.current.provider ?? null,
runtime: agentActivityRuntime,
workspaceId
});
const recoveredAgentSessionId =
dataRef.current.lastActiveAgentSessionId?.trim() ?? "";
if (recoveredAgentSessionId) {
reportAgentGUISubmitRecoveredActiveConversation({
blockCount: normalizedContent.length,
conversationCount: conversationsRef.current.length,
conversationListQueryReady: conversationListQuery !== null,
promptLength,
provider: dataRef.current.provider ?? null,
recoveredAgentSessionId,
runtime: agentActivityRuntime,
workspaceId
});
activeConversationIdRef.current = recoveredAgentSessionId;
setActiveConversationId(recoveredAgentSessionId);
setIntent({ tag: "active", id: recoveredAgentSessionId });
persistActiveConversation(recoveredAgentSessionId);
if (typedGoal) {
goalControl(
typedGoal.action,
typedGoal.objective,
resolveAgentComposerDraftScopeKey({})
);
return;
}
submitExistingPrompt(
recoveredAgentSessionId,
normalizedContent,
displayPromptText,
{
capabilityRefs: options?.capabilityRefs,
requiredSettingsPatch: options?.requiredSettingsPatch,
submittedDraft: options?.submittedDraft,
sourceScopeKey: resolveAgentComposerDraftScopeKey({}),
trackDraft: true
}
);
return;
}
}
const homeDraftKey = resolveAgentComposerDraftScopeKey({});
const submittedHomeDraft = snapshotAgentComposerDraft(
options?.submittedDraft ??
draftByScopeKeyRef.current[homeDraftKey] ??
emptyAgentComposerDraft()
);
const activationResult = startConversation(
normalizedContent,
displayPromptText,
options,
typedGoal ? false : undefined,
typedGoal ?? undefined
);
if (activationResult) {
draftByScopeKeyRef.current = clearSubmittedAgentGUIHomeDraft({
draftKey: homeDraftKey,
drafts: draftByScopeKeyRef.current,
submittedDraft: submittedHomeDraft
});
setDraftByScopeKey((current) =>
clearSubmittedAgentGUIHomeDraft({
draftKey: homeDraftKey,
drafts: current,
submittedDraft: submittedHomeDraft
})
);
}
return;
}
if (typedGoal) {
goalControl(
typedGoal.action,
typedGoal.objective,
resolveAgentComposerDraftScopeKey({ agentSessionId })
);
return;
}
const activeTurnId = activeEngineActiveTurn?.turnId.trim() ?? "";
const pendingQuestionResponse = resolvePendingQuestionComposerResponse({
activeTurnId,
agentSessionId,
content: normalizedContent,
pendingInteractions: activeEnginePendingInteractions,
submitOptions: options
});
if (pendingQuestionResponse) {
const sourceScopeKey = resolveAgentComposerDraftScopeKey({
agentSessionId
});
const submittedDraftSnapshot: SubmittedDraftSnapshot = {
content: snapshotAgentComposerDraft(
options?.submittedDraft ??
draftByScopeKeyRef.current[sourceScopeKey] ??
emptyAgentComposerDraft()
),
sourceScopeKey,
targetAgentSessionId: agentSessionId
};
const settlementKey = agentGUIInteractionDraftSettlementKey({
agentSessionId,
requestId: pendingQuestionResponse.requestId,
turnId: activeTurnId
});
interactionDraftSettlementsRef.current[settlementKey] = {
agentSessionId,
requestId: pendingQuestionResponse.requestId,
submittedDraftSnapshot,
turnId: activeTurnId
};
if (!submitInteractivePrompt(pendingQuestionResponse)) {
delete interactionDraftSettlementsRef.current[settlementKey];
return;
}
setDraftByScopeKey((current) => {
const next = clearSubmittedDraftIfUnchanged({
drafts: current,
snapshot: submittedDraftSnapshot
});
draftByScopeKeyRef.current = next;
return next;
});
return;
}
submitExistingPrompt(
agentSessionId,
normalizedContent,
displayPromptText,
{
capabilityRefs: options?.capabilityRefs,
requiredSettingsPatch: options?.requiredSettingsPatch,
submittedDraft: options?.submittedDraft,
trackDraft: true
}
);
},
[
activeEngineActiveTurn,
activeEnginePendingInteractions,
agentActivityRuntime,
conversationListQuery,
goalControl,
goalControlSupported,
persistActiveConversation,
promptImagesSupported,
startConversation,
submitExistingPrompt,
submitInteractivePrompt,
workspaceId
]
);
useEffect(() => {
submitPromptRef.current = submitPrompt;
}, [submitPrompt]);
const submitGuidancePrompt = useCallback(
(
content: AgentPromptContentBlock[],
displayPrompt?: string,
options?: AgentComposerSubmitOptions
) => {
const agentSessionId = activeConversationIdRef.current;
const normalizedContent = normalizeAgentPromptContentBlocks(content);
if (!agentSessionId || normalizedContent.length === 0) {
return;
}
if (
!promptImagesSupported &&
agentPromptContentHasImage(normalizedContent)
) {
setDetailError(translate("agentHost.agentGui.promptImagesUnsupported"));
return;
}
const activeTurnId = activeEngineActiveTurn?.turnId.trim() ?? "";
if (activeTurnId === "") {
return;
}
const displayPromptText =
displayPrompt && displayPrompt.trim() ? displayPrompt : undefined;
submitExistingPrompt(
agentSessionId,
normalizedContent,
displayPromptText,
{
capabilityRefs: options?.capabilityRefs,
sendNow: true,
submittedDraft: options?.submittedDraft,
targetTurnId: activeTurnId,
trackDraft: true
}
);
},
[
activeEngineActiveTurn,
promptImagesSupported,
submitExistingPrompt,
translate
]
);
const showPromptImagesUnsupported = useCallback(() => {
setDetailError(translate("agentHost.agentGui.promptImagesUnsupported"));
}, []);
const submitApprovalOption = useCallback(
(input: AgentInteractionResponseInput): boolean =>
submitInteractivePrompt(input),
[submitInteractivePrompt]
);
const interruptCurrentTurn = useCallback(
(noRunningResponseMessage: string) => {
const agentSessionId = activeConversationIdRef.current;
if (!agentSessionId) return;
void noRunningResponseMessage;
setDetailError(null);
sessionEngine.stopSession({ agentSessionId });
},
[sessionEngine]
);
const updateDraftContent = useCallback(
(draftContent: AgentComposerDraft, sourceScopeKey?: string) => {
const agentSessionId = activeConversationIdRef.current;
const draftKey =
sourceScopeKey ??
resolveAgentComposerDraftScopeKey({
agentSessionId
});
draftByScopeKeyRef.current = {
...draftByScopeKeyRef.current,
[draftKey]: draftContent
};
setDraftByScopeKey((current) => ({
...current,
[draftKey]: draftContent
}));
},
[]
);
return {
goalControl,
interruptCurrentTurn,
retryActivation,
showPromptImagesUnsupported,
submitApprovalOption,
submitGuidancePrompt,
submitInteractivePrompt,
submitPrompt,
updateDraftContent
};
}