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

225 lines
7.4 KiB
TypeScript

import type { AgentActivityMessage } from "@tutti-os/agent-activity-core";
import { AGENT_PROVIDER_LABEL } from "../../../contexts/settings/domain/agentSettings";
import { translate } from "../../../i18n/index";
import { isWorkspaceAgentActivityOptimisticMessage } from "../../../shared/workspaceAgentMessageOverlay";
import {
createAgentSessionMentionHref,
formatAgentMentionMarkdown,
normalizeAgentSessionMentionTitle
} from "../agentRichText/agentFileMentionExtension";
import { type AgentGUIConversationSummary } from "../model/agentGuiConversationModel";
import { resolveAgentGUIExplicitConversationTitle } from "../model/agentGuiProviderIdentity";
import { stringPayloadValue } from "./agentGuiController.promptHelpers";
import { AGENT_GUI_SUBMIT_RETARGET_EARLY_MESSAGE_TOLERANCE_MS } from "./agentGuiController.providerHelpers";
import { createPendingOptimisticTurnId } from "./agentGuiController.draftMessageHelpers";
interface AgentSubmitTraceState {
clientSubmitId: string;
startedAtUnixMs: number;
turnId: string | null;
}
export {
normalizePermissionModeSemantic,
permissionConfigFromComposerOptions,
permissionModeDescription,
permissionModeLabel,
permissionModeOptions
} from "./agentGuiController.composerHelpers";
export {
agentGUIConversationDiagnosticDetails,
agentGUIRuntimeSessionDiagnosticDetails,
agentGUISessionStateDiagnosticDetails,
agentGUIToolCallStatusIsWaiting,
promptRequestId
} from "./agentGuiController.diagnostics";
export * from "./agentGuiController.errors";
export {
createAgentGUIConversationId,
normalizeOptionalPrompt,
normalizeOptionalText,
projectAgentGUIMessagesToTimelineItems,
recordValue,
stringPayloadValue
} from "./agentGuiController.promptHelpers";
export * from "./agentGuiController.providerHelpers";
export {
messageFromMessageUpdate,
normalizeTimelineStatus,
normalizedPositiveNumber,
timelineItemTime
} from "./agentGuiController.sessionHelpers";
export {
filterMessagesForDetailWindowOverlay,
maxFiniteMessageVersion,
minFiniteMessageVersion,
sessionHasRenderableMessages,
windowHasTurnMissingUserPrompt
} from "./useAgentConversationMessagePaging";
export function buildContinueInNewConversationPrompt(input: {
workspaceId: string;
agentSessionId: string;
conversationUserId?: string | null;
currentUserId?: string | null;
userProfilesByUserId: Record<string, { name?: string | null }>;
provider: string;
agentTargetId?: string | null;
conversationTitle: string;
existingDraftPrompt: string;
}): string {
const providerLabelFromCatalog =
AGENT_PROVIDER_LABEL[input.provider as keyof typeof AGENT_PROVIDER_LABEL] ??
null;
const providerLabel =
providerLabelFromCatalog ||
input.provider.trim() ||
translate("sidebar.fallbackAgentLabel");
const normalizedTitle = normalizeAgentSessionMentionTitle(
input.conversationTitle
);
const normalizedConversationUserId = input.conversationUserId?.trim() ?? "";
const normalizedCurrentUserId = input.currentUserId?.trim() ?? "";
const initiatorName =
(normalizedConversationUserId &&
input.userProfilesByUserId[normalizedConversationUserId]?.name?.trim()) ||
(normalizedCurrentUserId &&
input.userProfilesByUserId[normalizedCurrentUserId]?.name?.trim()) ||
normalizedConversationUserId ||
normalizedCurrentUserId ||
translate("messages.agentThisSessionMentionLabel").trim();
const mentionLabel = `${initiatorName} & ${providerLabel}${
normalizedTitle ? ` ${normalizedTitle}` : ""
}`.trim();
const href = createAgentSessionMentionHref({
agentTargetId: input.agentTargetId,
agentSessionId: input.agentSessionId,
label: mentionLabel,
workspaceId: input.workspaceId
});
const mention = formatAgentMentionMarkdown({
kind: "session",
href,
workspaceId: input.workspaceId,
targetId: input.agentSessionId,
agentTargetId: input.agentTargetId?.trim() || undefined,
name: mentionLabel,
title: normalizedTitle || providerLabel,
scope: "my_sessions",
initiatorName,
agentName: providerLabel
});
const existingDraftPrompt = input.existingDraftPrompt.trim();
if (!existingDraftPrompt) {
return `${mention} `;
}
if (existingDraftPrompt.includes(href)) {
return existingDraftPrompt;
}
return `${mention} ${existingDraftPrompt}`;
}
export function resolveConversationUpdatedAtUnixMsFromSessionState(input: {
currentUpdatedAtUnixMs: number;
snapshotUpdatedAtUnixMs?: number;
source?: "conversation-selected" | "activity-stream" | "settings-update";
}): number {
if (input.source === "conversation-selected") {
return input.currentUpdatedAtUnixMs;
}
const updatedAtUnixMs = input.snapshotUpdatedAtUnixMs ?? Date.now();
return Math.max(input.currentUpdatedAtUnixMs, updatedAtUnixMs);
}
export function mergeVisibleConversations(
conversations: readonly AgentGUIConversationSummary[],
transientConversation: AgentGUIConversationSummary | null
): AgentGUIConversationSummary[] {
if (!transientConversation) {
return [...conversations];
}
if (
conversations.some(
(conversation) => conversation.id === transientConversation.id
)
) {
return [...conversations];
}
return [transientConversation, ...conversations];
}
export function retargetOptimisticPromptMessages(
messages: readonly AgentActivityMessage[],
input: { clientSubmitId: string; turnId: string }
): { changed: boolean; messages: AgentActivityMessage[] } {
const clientSubmitId = input.clientSubmitId.trim();
const turnId = input.turnId.trim();
if (!clientSubmitId || !turnId || messages.length === 0) {
return { changed: false, messages: [...messages] };
}
const pendingTurnId = createPendingOptimisticTurnId(clientSubmitId);
let changed = false;
const retargeted = messages.map((message) => {
if (
!isWorkspaceAgentActivityOptimisticMessage(message) ||
message.turnId?.trim() !== pendingTurnId
) {
return message;
}
const messageClientSubmitId = message.payload?.clientSubmitId;
if (
typeof messageClientSubmitId === "string" &&
messageClientSubmitId.trim() &&
messageClientSubmitId.trim() !== clientSubmitId
) {
return message;
}
changed = true;
return { ...message, turnId };
});
return { changed, messages: retargeted };
}
export function shouldRetargetOptimisticPromptFromMessage(
message: AgentActivityMessage,
trace: AgentSubmitTraceState
): boolean {
const turnId = message.turnId?.trim() ?? "";
if (!turnId || trace.turnId) {
return false;
}
const clientSubmitId = stringPayloadValue(message.payload, "clientSubmitId");
if (clientSubmitId?.trim()) {
return clientSubmitId.trim() === trace.clientSubmitId;
}
if (message.role.trim().toLowerCase() === "user") {
return false;
}
const messageTimeUnixMs = messageActivityTimeUnixMs(message);
return (
messageTimeUnixMs === null ||
messageTimeUnixMs >=
trace.startedAtUnixMs -
AGENT_GUI_SUBMIT_RETARGET_EARLY_MESSAGE_TOLERANCE_MS
);
}
export function messageActivityTimeUnixMs(
message: AgentActivityMessage
): number | null {
for (const value of [
message.occurredAtUnixMs,
message.startedAtUnixMs,
message.completedAtUnixMs
]) {
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
return value;
}
}
return null;
}
export function hasPromptConversationTitle(
conversation: AgentGUIConversationSummary
): boolean {
return resolveAgentGUIExplicitConversationTitle(conversation) !== null;
}